[h5py] 240/455: Native Python exception support

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Thu Jul 2 18:19:37 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 6abd6c7643fa5f8a526186896b0237bcf09ad788
Author: andrewcollette <andrew.collette at gmail.com>
Date:   Wed Apr 22 00:30:29 2009 +0000

    Native Python exception support
---
 h5py/__init__.py |   3 +-
 h5py/_stub.py    |  14 ++++
 h5py/_sync.py    |   1 +
 h5py/h5.pyx      | 151 +----------------------------------------
 h5py/h5e.pxd     |   6 +-
 h5py/h5e.pyx     | 199 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 setup.py         |   2 +-
 7 files changed, 200 insertions(+), 176 deletions(-)

diff --git a/h5py/__init__.py b/h5py/__init__.py
index 434180f..0ea2184 100644
--- a/h5py/__init__.py
+++ b/h5py/__init__.py
@@ -32,7 +32,8 @@ except ImportError, e:
 import utils, h5, h5a, h5d, h5f, h5fd, h5g, h5i, h5p, h5r, h5s, h5t, h5z, highlevel, version
 
 from highlevel import File, Group, Dataset, Datatype, AttributeManager, is_hdf5, CoordsList
-from h5 import H5Error, get_config
+from h5 import get_config
+from h5e import H5Error
 
 import filters, selections
 
diff --git a/h5py/_stub.py b/h5py/_stub.py
new file mode 100644
index 0000000..aec11cc
--- /dev/null
+++ b/h5py/_stub.py
@@ -0,0 +1,14 @@
+
+# Cython has limits on what you can declare inside control structures.  This
+# native-Python module is a shim to allow things like dynamic class
+# definitions and functional closures.
+
+def generate_class(cls1, cls2):
+    """ Create a new class from two bases.  The new name is the concatenation
+    of cls2.__name__ with "H5"; e.g. KeyError -> KeyErrorH5.
+    """
+    class HybridClass(cls1, cls2):
+        pass
+    HybridClass.__name__ = cls2.__name__+"H5"
+    return HybridClass
+
diff --git a/h5py/_sync.py b/h5py/_sync.py
index 4e763c7..765e690 100644
--- a/h5py/_sync.py
+++ b/h5py/_sync.py
@@ -21,6 +21,7 @@ phil = get_phil()
 config = get_config()
 logger = logging.getLogger('h5py.functions')
 
+
 def uw_apply(wrap, func):
     # Cython methods don't have a "module" attribute for some reason
     if hasattr(func, '__module__'):
diff --git a/h5py/h5.pyx b/h5py/h5.pyx
index fe95423..d812da5 100644
--- a/h5py/h5.pyx
+++ b/h5py/h5.pyx
@@ -22,7 +22,8 @@ __doc__ = \
 
 include "config.pxi"
 
-from python_exc cimport PyErr_SetString
+
+from h5e cimport register_thread
 
 import atexit
 import threading
@@ -376,145 +377,6 @@ def _open():
 
 # === Public exception hierarchy ==============================================
 
-
-
-
-# === Error stack inspection ==================================================
-
-cdef class ErrorStackElement:
-    """
-        Represents an entry in the HDF5 error stack.
-        Modeled on the H5E_error_t struct.  All properties are read-only.
-
-        Attributes:
-
-        * maj_num:    INT major error number
-        * min_num:    INT minor error number
-        * func_name:  STRING name of failing function
-        * file_name:  STRING name of file in which error occurreed
-        * line:       UINT line number at which error occured
-        * desc:       STRING description of error
-    """
-    cdef readonly int maj_num
-    cdef readonly int min_num
-    cdef readonly object func_name
-    cdef readonly object file_name
-    cdef readonly unsigned int line
-    cdef readonly object desc
-
-    @sync
-    def __str__(self):
-        return '%2d:%2d "%s" at %s (%s: %s)' % (self.maj_num, self.min_num,
-                self.desc, self.func_name, H5Eget_major(<H5E_major_t>self.maj_num),
-                H5Eget_minor(<H5E_minor_t>self.min_num) )
-
-cdef herr_t walk_cb(int n, H5E_error_t *err_desc, void* stack_in):
-    # Callback function to extract elements from the HDF5 error stack
-
-    stack = <object>stack_in
-    cdef ErrorStackElement element
-
-    element = ErrorStackElement()
-    element.maj_num = err_desc.maj_num
-    element.min_num = err_desc.min_num
-    element.func_name = err_desc.func_name
-    element.file_name = err_desc.file_name
-    element.desc = err_desc.desc
-
-    stack.append(element)
-
-    return 0
-
- at sync
-def error_stack():
-    """ () => LIST error_stack
-
-        Retrieve the HDF5 error stack as a list of ErrorStackElement objects,
-        with the most recent call (the deepest one) listed last.
-    """
-    stack = []
-    H5Ewalk(H5E_WALK_DOWNWARD, walk_cb, <void*>stack)
-    return stack
-
-cpdef object error_string():
-    """ () => STRING error_stack
-
-        Return a string representation of the current error condition.
-        Format is one line of the format::
-
-            '<Description> (<Function name>: <error type>)'
-
-        If the stack is more than one level deep, this is followed by a
-        header and then n lines of the format::
-
-            '    n: "<Description>" at <function name>'
-    """
-    cdef int stacklen
-    cdef ErrorStackElement el
-
-    stack = error_stack()
-    stacklen = len(stack)
-
-    if stacklen == 0:
-        msg = "No HDF5 error recorded"
-    else:
-        el = stack[0]
-        msg = "%s (%s)" % (el.desc.capitalize(), el.func_name)
-        if stacklen > 1:
-            msg = msg + "\nHDF5 Error Stack:"
-            for i from 0<=i<stacklen:
-                #msg = msg + '\n' + str(stack[i])
-                el = stack[i]
-                maj_msg = H5Eget_major(<H5E_major_t>el.maj_num)
-                min_msg = H5Eget_minor(<H5E_minor_t>el.min_num)
-                msg = msg + '\n    %d: "%s" at %s [%s :: %s]' % \
-                            (i, el.desc.capitalize(), el.func_name, maj_msg, min_msg)
-
-    return msg
-
- at sync
-def clear():
-    """ ()
-
-        Clear the error stack.
-    """
-    H5Eclear()
-
-# === Automatic exception API =================================================
-
-cdef herr_t extract_cb(int n, H5E_error_t *err_desc, void* data_in):
-    # Callback to determine error information at top/bottom of stack
-    cdef H5E_error_t *err_struct
-    err_struct = <H5E_error_t*>data_in
-    err_struct.maj_num = err_desc.maj_num
-    err_struct.min_num = err_desc.min_num
-    return 1
-    
-cdef herr_t err_callback(void* client_data) with gil:
-    # Callback which sets Python exception based on the current error stack.
-
-    # MUST be "with gil" as it can be called by nogil HDF5 routines.
-    # By definition any function for which this can be called already
-    # holds the PHIL.
-    
-    cdef H5E_error_t err_struct
-    cdef H5E_major_t mj
-
-    # Determine the error numbers for the first entry on the stack.
-    H5Ewalk(H5E_WALK_UPWARD, extract_cb, &err_struct)
-    mj = err_struct.maj_num
-
-    try:
-        exc = _exceptions[mj]
-    except KeyError:
-        exc = H5Error
-
-    msg = error_string()
-    PyErr_SetString(exc, msg)  # Can't use "raise" or the traceback points here
-
-    return 1
-
-
 # === Library init ============================================================
 
 def _exithack():
@@ -545,13 +407,6 @@ def _exithack():
 
 hdf5_inited = 0
 
-cpdef int register_thread() except -1:
-    """ Register the current thread for native HDF5 exception support.
-    """
-    if H5Eset_auto(err_callback, NULL) < 0:
-        raise RuntimeError("Failed to register HDF5 exception callback")
-    return 0
-
 cdef int init_hdf5() except -1:
     # Initialize the library and register Python callbacks for exception
     # handling.  Safe to call more than once.
@@ -580,6 +435,6 @@ _version_tuple = tuple([int(x) for x in H5PY_VERSION.split('.')])
 
 
 
-
+from h5e import H5Error
 
 
diff --git a/h5py/h5e.pxd b/h5py/h5e.pxd
index 04c10e4..2cedcdf 100644
--- a/h5py/h5e.pxd
+++ b/h5py/h5e.pxd
@@ -8,6 +8,10 @@ include "defs.pxd"
 
 # === H5E - Error handling API ================================================
 
+cdef herr_t err_callback(void* client_data) with gil
+
+cpdef int register_thread() except -1
+
 cdef extern from "hdf5.h":
 
   # Major error numbers
@@ -15,7 +19,7 @@ cdef extern from "hdf5.h":
     H5E_NONE_MAJOR       = 0,   # special zero, no error                     
     H5E_ARGS,                   # invalid arguments to routine               
     H5E_RESOURCE,               # resource unavailable                       
-    H5E_INTERNAL,               #  Internal error (too specific to document)
+    H5E_INTERNAL,               # Internal error (too specific to document)
     H5E_FILE,                   # file Accessability                         
     H5E_IO,                     # Low-level I/O                              
     H5E_FUNC,                   # function Entry/Exit                        
diff --git a/h5py/h5e.pyx b/h5py/h5e.pyx
index 3ed2f07..0904418 100644
--- a/h5py/h5e.pyx
+++ b/h5py/h5e.pyx
@@ -1,11 +1,15 @@
 
 """
-    New exceptions module!
+    Implements HDF5 error support and Python exception translation.
 """
 
 
 include "config.pxi"
-include "defs.pxd"
+
+
+from python_exc cimport PyErr_SetString
+from h5 cimport SmartStruct
+import _stub
 
 class H5Error(Exception):
     """ Base class for internal HDF5 library exceptions.
@@ -142,6 +146,8 @@ class SkipListError(H5Error):
     """ H5E_SLIST """
     pass  
 
+# Traditional major error classes.  One of these (or H5Error) is always a
+# parent class of the raised exception.
 cdef dict _major_table = {
     H5E_ARGS: ArgsError,
     H5E_RESOURCE: ResourceError,
@@ -170,39 +176,182 @@ cdef dict _major_table = {
     H5E_ERROR: ErrorError,
     H5E_SLIST: SkipListError}
 
+# Python-style minor error classes.  If the minor error code matches an entry
+# in this dict, the generated exception will also descend from the indicated
+# built-in exception.
 cdef dict _minor_table = {
-    H5E_SEEKERROR:    IOError,       # Seek failed 
-    H5E_READERROR:    IOError,       # Read failed  
-    H5E_WRITEERROR:   IOError,       # Write failed  
-    H5E_CLOSEERROR:   IOError,       # Close failed 
-    H5E_OVERFLOW:     IOError,       # Address overflowed 
-    H5E_FCNTL:        IOError,       # File control (fcntl) failed
-
-    H5E_FILEEXISTS:   ValueError,    # File already exists 
-    H5E_FILEOPEN:     ValueError,    # File already open 
-    H5E_NOTHDF5:      ValueError,    # Not an HDF5 file 
-    H5E_BADFILE:      ValueError,    # Bad file ID accessed
-
-    H5E_BADATOM:      KeyError,      # Unable to find atom information (already closed?) 
-    H5E_BADGROUP:     KeyError,      # Unable to find ID group information 
-    H5E_BADSELECT:    ValueError,    # Invalid selection (hyperslabs)
+    H5E_SEEKERROR:      IOError,    # Seek failed 
+    H5E_READERROR:      IOError,    # Read failed  
+    H5E_WRITEERROR:     IOError,    # Write failed  
+    H5E_CLOSEERROR:     IOError,    # Close failed 
+    H5E_OVERFLOW:       IOError,    # Address overflowed 
+    H5E_FCNTL:          IOError,    # File control (fcntl) failed
+
+    H5E_NOFILTER:       IOError,    # Requested filter is not available 
+    H5E_CALLBACK:       IOError,    # Callback failed 
+    H5E_CANAPPLY:       IOError,    # Error from filter 'can apply' callback 
+    H5E_SETLOCAL:       IOError,    # Error from filter 'set local' callback 
+    H5E_NOENCODER:      IOError,    # Filter present but encoding disabled 
+
+    H5E_FILEEXISTS:     ValueError,  # File already exists 
+    H5E_FILEOPEN:       ValueError,  # File already open 
+    H5E_NOTHDF5:        ValueError,  # Not an HDF5 file 
+    H5E_BADFILE:        ValueError,  # Bad file ID accessed
+
+    H5E_BADATOM:        ValueError,  # Unable to find atom information (already closed?) 
+    H5E_BADGROUP:       ValueError,  # Unable to find ID group information 
+    H5E_BADSELECT:      ValueError,  # Invalid selection (hyperslabs)
     H5E_UNINITIALIZED:  ValueError,  # Information is uinitialized 
-    H5E_UNSUPPORTED:  NotImplementedError,    # Feature is unsupported 
-
-    H5E_BADTYPE:      TypeError,     # Inappropriate type 
-    H5E_BADRANGE:     ValueError,    # Out of range 
-    H5E_BADVALUE:     ValueError,    # Bad value
-    H5E_NOTFOUND:     KeyError,      # Object not found 
-    H5E_EXISTS:       ValueError,    # Object already exists 
-    H5E_CANTCONVERT:  TypeError      # Can't convert datatypes 
+    H5E_UNSUPPORTED:    NotImplementedError,    # Feature is unsupported 
+
+    H5E_NOTFOUND:       KeyError,    # Object not found 
+    H5E_CANTINSERT:     TypeError,   # Unable to insert object 
+
+    H5E_BADTYPE:        TypeError,   # Inappropriate type 
+    H5E_BADRANGE:       ValueError,  # Out of range 
+    H5E_BADVALUE:       ValueError,  # Bad value
+
+    H5E_EXISTS:         ValueError,  # Object already exists 
+    H5E_CANTCONVERT:    TypeError    # Can't convert datatypes 
   }
 
+# "Fudge" table to accomodate annoying inconsistencies in HDF5's use 
+# of the minor error codes.  If a (major, minor) entry appears here,
+# it will override any entry in the minor error table.
+cdef dict _exact_table = {
+    (H5E_CACHE, H5E_BADVALUE):      IOError,  # obj create w/o write intent 1.8
+    (H5E_RESOURCE, H5E_CANTINIT):   IOError,  # obj create w/o write intent 1.6
+    (H5E_INTERNAL, H5E_SYSERRSTR):  IOError,  # e.g. wrong file permissions
+  }
 
+# === Error stack inspection ==================================================
 
 
+cdef class ErrorStackElement(SmartStruct):
 
+    """
+        Encapsulation of the H5E_error_t structure.
+    """
+    
+    cdef H5E_error_t e
+
+    property func_name:
+        """ Name of HDF5 C function in which error occurred """
+        def __get__(self):
+            return self.e.func_name
+    property desc:
+        """ Brief description of the error """
+        def __get__(self):
+            s = self.e.desc
+            return s.capitalize()
+    property code:
+        """ A 2-tuple of error codes (major, minor) """
+        def __get__(self):
+            return (<int>self.e.maj_num, <int>self.e.min_num)
+    property code_desc:
+        """ A 2-tuple of strings (major description, minor description) """
+        def __get__(self):
+            return H5Eget_major(self.e.maj_num), H5Eget_minor(self.e.min_num)
+
+def get_major(int code):
+    """ Get description for a major error code """
+    return H5Eget_major(<H5E_major_t>code)
+
+def get_minor(int code):
+    """ Get description for a minor error code """
+    return H5Eget_minor(<H5E_minor_t>code)
+
+_verbose = False
+def verbose(bint v):
+    global _verbose
+    _verbose = bool(v)
+
+class ErrorStack(list):
+
+    """
+        Represents the HDF5 error stack
+    """
 
+    def __init__(self, *args, **kwds):
+        list.__init__(self, *args, **kwds)
+        H5Ewalk(H5E_WALK_UPWARD, walk_cb, <void*>self)
 
+    def get_exc_msg(self):
+        """ Returns a 2-tuple (exception class, string message) representing
+            the current error condition, or None if no error exists.
+        """
+        global _verbose
 
+        if len(self) == 0:
+            return None
+
+        major, minor = self[0].code
+
+        maj_class = _major_table.get(major, H5Error)
+        min_class = _exact_table.get((major, minor), None)
+        if min_class is None:
+            min_class = _minor_table.get(minor, None)
+
+        if min_class is None:
+            exc = maj_class
+        else:
+            exc = _stub.generate_class(maj_class, min_class)
+
+        msg = "%s (%s: %s)" % (self[0].desc, self[0].code_desc[0],
+                                                   self[0].code_desc[1])
+        if _verbose:
+            msg += '\n'+str(self)
+
+        return exc, msg
+ 
+    def __str__(self):
+        """ Return a string representation of the error stack """
+        if len(self) == 0:
+            return "No HDF5 error recorded"
+        s = 'HDF5 Error Stack:'
+        for idx, el in enumerate(self):
+            s += '\n    %d: "%s" at %s' % (idx, el.desc, el.func_name)
+            s += '\n        %s :: %s' % el.code_desc
+
+        return s
+
+cdef herr_t walk_cb(int n, H5E_error_t *err_desc, void* stack_in):
+    # Callback function to extract elements from the HDF5 error stack
+
+    stack = <object>stack_in
+
+    cdef ErrorStackElement element
+    element = ErrorStackElement()
+
+    element.e = err_desc[0]
+    stack.append(element)
+
+    return 0
+
+cdef herr_t err_callback(void* client_data) with gil:
+    # Callback which sets Python exception based on the current error stack.
+
+    # MUST be "with gil" as it can be called by nogil HDF5 routines.
+    # By definition any function for which this can be called already
+    # holds the PHIL.
+    
+    stack = ErrorStack()
+
+    a = stack.get_exc_msg()
+    if a is None:
+        exc, msg = RuntimeError, "No HDF5 exception information found"
+    else:
+        exc, msg = a
+
+    PyErr_SetString(exc, msg)  # Can't use "raise" or the traceback points here
+
+    return 1
+
+cpdef int register_thread() except -1:
+    """ Register the current thread for native HDF5 exception support.
+    """
+    if H5Eset_auto(err_callback, NULL) < 0:
+        raise RuntimeError("Failed to register HDF5 exception callback")
+    return 0
 
 
diff --git a/setup.py b/setup.py
index a6fcdb0..b4edf09 100644
--- a/setup.py
+++ b/setup.py
@@ -56,7 +56,7 @@ def debug(instring):
 def localpath(*args):
     return op.abspath(reduce(op.join, (op.dirname(__file__),)+args))
 
-MODULES = ['h5', 'h5f', 'h5g', 'h5s', 'h5t', 'h5d', 'h5a', 'h5p', 'h5z',
+MODULES = ['h5', 'h5e', 'h5f', 'h5g', 'h5s', 'h5t', 'h5d', 'h5a', 'h5p', 'h5z',
                  'h5i', 'h5r', 'h5fd', 'utils', 'h5o', 'h5l']
 
 EXTRA_SRC = {'h5': [ localpath("lzf/lzf_filter.c"), 

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