[h5py] 122/455: Modify module h5 for PHIL, decorators

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Thu Jul 2 18:19:24 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 787e7800e1294e44caf5c1c4bf10c103b00e20c8
Author: andrewcollette <andrew.collette at gmail.com>
Date:   Mon Sep 22 01:16:54 2008 +0000

    Modify module h5 for PHIL, decorators
---
 h5py/_extras.py   |  10 +-
 h5py/h5.pxd       | 197 +++++----------------
 h5py/h5.pyx       | 243 ++++++++++++-------------
 h5py/h5a.pyx      |  18 +-
 h5py/h5t.pyx      |   7 +-
 h5py/std_code.pxi |  36 ++--
 setup.py          | 520 ++++++++++++++++++++++++++++--------------------------
 setup_cython.py   | 430 --------------------------------------------
 8 files changed, 477 insertions(+), 984 deletions(-)

diff --git a/h5py/_extras.py b/h5py/_extras.py
index c80abd1..1cbcc94 100644
--- a/h5py/_extras.py
+++ b/h5py/_extras.py
@@ -12,11 +12,11 @@
 from __future__ import with_statement
 
 import logging
-from h5py import config
-
-# Decorator utility for low-level thread safety
 from functools import update_wrapper
 
+from h5py.h5 import get_phil
+phil = get_phil()
+
 def uw_apply(wrap, func):
     # Cython methods don't have a "module" attribute for some reason
     if hasattr(func, '__module__'):
@@ -30,7 +30,7 @@ def h5sync(logger=None):
         def sync_simple(func):
             
             def wrap(*args, **kwds):
-                with config.lock:
+                with phil:
                     return func(*args, **kwds)
 
             uw_apply(wrap, func)
@@ -44,7 +44,7 @@ def h5sync(logger=None):
 
             def wrap(*args, **kwds):
                 logger.debug("$ Threadsafe function entry: %s" % func.__name__)
-                with config.lock:
+                with phil:
                     logger.debug("> Acquired lock on %s" % func.__name__)
                     retval = func(*args, **kwds)
                 logger.debug("< Released lock on %s" % func.__name__)
diff --git a/h5py/h5.pxd b/h5py/h5.pxd
index d2717fe..3a0571d 100644
--- a/h5py/h5.pxd
+++ b/h5py/h5.pxd
@@ -16,6 +16,43 @@
 
 include "std_defs.pxi"
 
+# === Custom C extensions =====================================================
+
+cdef int _enable_exceptions() except -1
+cdef int _disable_exceptions() except -1
+
+cdef object standard_richcmp(object self, object other, int how)
+
+cdef class PHIL:
+
+    cdef object lock
+
+    cpdef int __enter__(self) except -1
+    cpdef int __exit__(self, a, b, c) except -1
+    cpdef int acquire(self) except -1
+    cpdef int release(self) except -1
+
+cpdef PHIL get_phil()
+
+cdef class H5PYConfig:
+
+    # Global 
+    cdef object _complex_names      # ('r','i')
+    cdef readonly object API_16
+    cdef readonly object API_18
+    cdef readonly object DEBUG
+    cdef readonly object THREADS
+
+cdef class ObjectID:
+    """ Base wrapper class for HDF5 object identifiers """
+    cdef object __weakref__
+    cdef readonly hid_t id
+    cdef readonly int _locked
+    cdef object _hash           # Used by subclasses to cache a hash value,
+                                # which may be expensive to compute.
+
+# === HDF5 API ================================================================
+
 cdef extern from "hdf5.h":
 
   ctypedef struct hvl_t:
@@ -27,11 +64,11 @@ cdef extern from "hdf5.h":
   herr_t H5open() except *
   herr_t H5close() except *
 
-  # reflection
-  ctypedef enum H5I_type_t:
-    H5I_BADID        = -1,  # /*invalid Group                    */
 
   # --- Reflection ------------------------------------------------------------
+  ctypedef enum H5I_type_t:
+    H5I_BADID        = -1
+
   H5I_type_t H5Iget_type(hid_t obj_id) except *
   int        H5Idec_ref(hid_t obj_id) except *
   int        H5Iget_ref(hid_t obj_id) except *
@@ -74,132 +111,8 @@ cdef extern from "hdf5.h":
     H5E_ERROR,                  # Error API                                  
     H5E_SLIST                   # Skip Lists                                 
 
-  # Minor error numbers
   ctypedef enum H5E_minor_t:
-    H5E_NONE_MINOR       = 0
-
-    #  Argument errors 
-    H5E_UNINITIALIZED,          # information is unitialized
-    H5E_UNSUPPORTED,            # feature is unsupported
-    H5E_BADTYPE,                # incorrect type found
-    H5E_BADRANGE,               # argument out of range
-    H5E_BADVALUE,               # bad value for argument
-
-    #  Resource errors 
-    H5E_NOSPACE,                # no space available for allocation
-    H5E_CANTCOPY,               # unable to copy object
-    H5E_CANTFREE,               # unable to free object
-    H5E_ALREADYEXISTS,          # Object already exists
-    H5E_CANTLOCK,               # Unable to lock object
-    H5E_CANTUNLOCK,             # Unable to unlock object
-    H5E_CANTGC,                 # Unable to garbage collect
-    H5E_CANTGETSIZE,            # Unable to compute size
-
-    #  File accessability errors 
-    H5E_FILEEXISTS,             # file already exists                        
-    H5E_FILEOPEN,               # file already open                          
-    H5E_CANTCREATE,             # Can't create file                          
-    H5E_CANTOPENFILE,           # Can't open file                            
-    H5E_CANTCLOSEFILE,          # Can't close file                 
-    H5E_NOTHDF5,                # not an HDF5 format file                    
-    H5E_BADFILE,                # bad file ID accessed                       
-    H5E_TRUNCATED,              # file has been truncated                    
-    H5E_MOUNT,                    # file mount error                 
-
-    #  Generic low-level file I/O errors
-    H5E_SEEKERROR,              # seek failed                                
-    H5E_READERROR,              # read failed                                
-    H5E_WRITEERROR,             # write failed                               
-    H5E_CLOSEERROR,             # close failed                               
-    H5E_OVERFLOW,                # address overflowed                 
-    H5E_FCNTL,                  # file fcntl failed                          
-
-    #  Function entry/exit interface errors 
-    H5E_CANTINIT,               # Can't initialize object                    
-    H5E_ALREADYINIT,            # object already initialized                 
-    H5E_CANTRELEASE,            # Can't release object                       
-
-    #  Object atom related errors 
-    H5E_BADATOM,                # Can't find atom information                
-    H5E_BADGROUP,               # Can't find group information               
-    H5E_CANTREGISTER,           # Can't register new atom                    
-    H5E_CANTINC,                # Can't increment reference count            
-    H5E_CANTDEC,                # Can't decrement reference count            
-    H5E_NOIDS,                  # Out of IDs for group                       
-
-    #  Cache related errors 
-    H5E_CANTFLUSH,              # Can't flush object from cache              
-    H5E_CANTSERIALIZE,          # Unable to serialize data from cache        
-    H5E_CANTLOAD,               # Can't load object into cache               
-    H5E_PROTECT,                # protected object error                     
-    H5E_NOTCACHED,              # object not currently cached                
-    H5E_SYSTEM,                 # Internal error detected                    
-    H5E_CANTINS,                # Unable to insert metadata into cache       
-    H5E_CANTRENAME,             # Unable to rename metadata                  
-    H5E_CANTPROTECT,            # Unable to protect metadata                 
-    H5E_CANTUNPROTECT,          # Unable to unprotect metadata               
-
-    #  B-tree related errors 
-    H5E_NOTFOUND,               # object not found                           
-    H5E_EXISTS,                 # object already exists                      
-    H5E_CANTENCODE,             # Can't encode value                         
-    H5E_CANTDECODE,             # Can't decode value                         
-    H5E_CANTSPLIT,              # Can't split node                           
-    H5E_CANTINSERT,             # Can't insert object                        
-    H5E_CANTLIST,               # Can't list node                            
-
-    #  Object header related errors 
-    H5E_LINKCOUNT,              # bad object header link count               
-    H5E_VERSION,                # wrong version number                       
-    H5E_ALIGNMENT,              # alignment error                            
-    H5E_BADMESG,                # unrecognized message                       
-    H5E_CANTDELETE,             #  Can't delete message                      
-    H5E_BADITER,                #  Iteration failed                          
-
-    #  Group related errors 
-    H5E_CANTOPENOBJ,            # Can't open object                          
-    H5E_CANTCLOSEOBJ,           # Can't close object                         
-    H5E_COMPLEN,                # name component is too long                 
-    H5E_LINK,                   # link count failure                         
-    H5E_SLINK,                    # symbolic link error                 
-    H5E_PATH,                    # Problem with path to object             
-
-    #  Datatype conversion errors 
-    H5E_CANTCONVERT,            # Can't convert datatypes  TypeError?
-    H5E_BADSIZE,                # Bad size for object                        
-
-    #  Dataspace errors 
-    H5E_CANTCLIP,               # Can't clip hyperslab region                
-    H5E_CANTCOUNT,              # Can't count elements                       
-    H5E_CANTSELECT,             # Can't select hyperslab                     
-    H5E_CANTNEXT,               # Can't move to next iterator location       
-    H5E_BADSELECT,              # Invalid selection                          
-    H5E_CANTCOMPARE,            # Can't compare objects                      
-
-    #  Property list errors 
-    H5E_CANTGET,                # Can't get value                            
-    H5E_CANTSET,                # Can't set value                            
-    H5E_DUPCLASS,               # Duplicate class name in parent class       
-
-    #  Parallel errors 
-    H5E_MPI,                    # some MPI function failed             
-    H5E_MPIERRSTR,                # MPI Error String                  
-
-    #  Heap errors 
-    H5E_CANTRESTORE,            # Can't restore condition                    
-
-    #  TBBT errors 
-    H5E_CANTMAKETREE,            # Can't create TBBT tree                     
-
-    #  I/O pipeline errors 
-    H5E_NOFILTER,               # requested filter is not available          
-    H5E_CALLBACK,               # callback failed                            
-    H5E_CANAPPLY,               # error from filter "can apply" callback     
-    H5E_SETLOCAL,               # error from filter "set local" callback     
-    H5E_NOENCODER,              #  Filter present, but encoding disabled     
-
-    #  System level errors 
-    H5E_SYSERRSTR               #  System error message                 
+    pass
 
   cdef enum H5E_direction_t:
     H5E_WALK_UPWARD    = 0  # begin deep, end at API function    
@@ -213,8 +126,6 @@ cdef extern from "hdf5.h":
     unsigned    line        #  line in file where error occurs    
     char    *desc           #  optional supplied description      
 
-  # --- Error handling --------------------------------------------------------
-
   char      *H5Eget_major(H5E_major_t n)
   char      *H5Eget_minor(H5E_minor_t n)
   herr_t    H5Eclear() except *
@@ -228,33 +139,7 @@ cdef extern from "hdf5.h":
   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
-cdef int _enable_exceptions() except -1
-cdef int _disable_exceptions() except -1
-
-cdef err_c pause_errors() except? NULL
-cdef int resume_errors(err_c cookie) except -1
-
-cdef object standard_richcmp(object self, object other, int how)
-
-cdef class H5PYConfig:
 
-    cdef object _complex_names      # ('r','i')
-    cdef object _lock               # Primary HDF5 Interface Lock (PHIL)
-    cdef readonly object API_16
-    cdef readonly object API_18
-    cdef readonly object DEBUG
-    cdef readonly object THREADS
-
-cdef class ObjectID:
-    """ Base wrapper class for HDF5 object identifiers """
-    cdef object __weakref__
-    cdef readonly hid_t id
-    cdef readonly int _locked
-    cdef object _hash           # Used by subclasses to cache a hash value,
-                                # which may be expensive to compute.
 
 
 
diff --git a/h5py/h5.pyx b/h5py/h5.pyx
index 4ed3f29..8ef1678 100644
--- a/h5py/h5.pyx
+++ b/h5py/h5.pyx
@@ -35,7 +35,7 @@ from python cimport PyErr_SetObject
 
 import atexit
 import threading
-from weakref import WeakKeyDictionary
+# --- Module init -------------------------------------------------------------
 
 # Logging is only enabled when compiled with H5PY_DEBUG nonzero
 IF H5PY_DEBUG:
@@ -45,98 +45,80 @@ IF H5PY_DEBUG:
     logger.addHandler(logging.StreamHandler())
     log_ident = logging.getLogger('h5py.identifiers')
 
-def get_libversion():
-    """ () => TUPLE (major, minor, release)
+# --- C extensions and classes ------------------------------------------------
 
-        Retrieve the HDF5 library version as a 3-tuple.
-    """
-    cdef unsigned int major
-    cdef unsigned int minor
-    cdef unsigned int release
-    cdef herr_t retval
-    
-    H5get_libversion(&major, &minor, &release)
+cdef object standard_richcmp(object self, object other, int how):
+    # This needs to be shared because of weird CPython quirks involving
+    # subclasses and the __hash__ method.
 
-    return (major, minor, release)
+    if how == 2 or how == 3:
+        
+        if not isinstance(self, ObjectID) and isinstance(other, ObjectID):
+            return NotImplemented
 
-def _close():
-    """ Internal function; do not call unless you want to lose all your data.
-    """
-    H5close()
+        eq = (hash(self) == hash(other))
 
-def _open():
-    """ Internal function; do not call unless you want to lose all your data.
-    """
-    H5open()
+        if how == 2:
+            return eq
+        return not eq
+
+    return NotImplemented
 
 cdef class H5PYConfig:
 
     """
         Global configuration object for the h5py package.
-
-        Properties:
-        lock            Global reentrant lock for threading support
-        compile_opts    Dictionary of compile-time flags
     """
 
     def __init__(self):
-        global logger
-        self._complex_names = ('r','i')
         self.API_16 = H5PY_16API
         self.API_18 = H5PY_18API
         self.DEBUG = H5PY_DEBUG
         self.THREADS = H5PY_THREADS
-        self._lock = threading.RLock()
-
-    property lock:
-        """ Reentrant lock instance; default is threading.RLock().
-            
-            Whatever you provide MUST support the Python context manager
-            protocol, and provide the methods acquire() and release().  It
-            also MUST be reentrant, or dataset reads/writes will deadlock.
-        """
-        def __get__(self):
-            return self._lock
-
-        def __set__(self, val):
-            if not (hasattr(val, 'acquire') and hasattr(val, 'release') and\
-                    hasattr(val, '__enter__') and hasattr(val, '__exit__')):
-                raise ValueError("Generated locks must provide __enter__, __exit__, acquire, release")
-            current_lock = self._lock
-            current_lock.acquire()
-            try:
-                self._lock = val
-            finally:
-                current_lock.release()
-
-    property complex_names:
-        """ Unused
-        """
-        def __get__(self):
-            return self._complex_names
-
-        def __set__(self, val):
-            # TODO: validation
-            self._complex_names = val
-
-
 
-cdef object standard_richcmp(object self, object other, int how):
-    # This needs to be shared because of weird CPython quirks involving
-    # subclasses and the __hash__ method.
-
-    if how == 2 or how == 3:
-        
-        if not isinstance(self, ObjectID) and isinstance(other, ObjectID):
-            return NotImplemented
+cdef class PHIL:
 
-        eq = (hash(self) == hash(other))
-
-        if how == 2:
-            return eq
-        return not eq
+    """
+        The Primary HDF5 Interface Lock (PHIL) is a global reentrant lock
+        which manages access to the library.  HDF5 is not guaranteed to 
+        be thread-safe, and certain callbacks in h5py can execute arbitrary
+        threaded Python code.  Therefore, in threading mode all routines
+        acquire this lock first.  With threading support disabled, the
+        object's methods do nothing.
+    """
 
-    return NotImplemented
+    IF H5PY_THREADS:
+        def __init__(self):
+            self.lock = threading.RLock()
+        cpdef int __enter__(self) except -1:
+            self.lock.acquire()
+            return 0
+        cpdef int __exit__(self,a,b,c) except -1:
+            self.lock.release()
+            return 0
+        cpdef int acquire(self) except -1:
+            self.lock.acquire()
+            return 0
+        cpdef int release(self) except -1:
+            self.lock.release()
+            return 0
+    ELSE:
+        cpdef int __enter__(self) except -1:
+            return 0
+        cpdef int __exit__(self,a,b,c) except -1:
+            return 0
+        cpdef int acquire(self) except -1:
+            return 0
+        cpdef int release(self) except -1:
+            return 0      
+
+cdef PHIL phil = PHIL()
+cpdef PHIL get_phil():
+    """ Obtain a reference to the PHIL. """
+    return phil
+
+# Now that the PHIL is defined we can import the decorator
+include "std_code.pxi"
 
 cdef class ObjectID:
 
@@ -163,8 +145,13 @@ cdef class ObjectID:
         """ Indicates whether or not this identifier points to an HDF5 object.
         """
         def __get__(self):
-            return H5Iget_type(self.id) != H5I_BADID
+            phil.acquire()
+            try:
+                return H5Iget_type(self.id) != H5I_BADID
+            finally:
+                phil.release()
 
+    
     def __nonzero__(self):
         """ Truth value for object identifiers (like _valid) """
         return self._valid
@@ -178,11 +165,16 @@ cdef class ObjectID:
 
     def __dealloc__(self):
         """ Automatically decrefs the ID, if it's valid. """
-        IF H5PY_DEBUG:
-            log_ident.debug("- %s" % str(self))
-        if (not self._locked) and self._valid:
-            H5Idec_ref(self.id)
+        phil.acquire()
+        try:
+            IF H5PY_DEBUG:
+                log_ident.debug("- %s" % str(self))
+            if (not self._locked) and self._valid:
+                H5Idec_ref(self.id)
+        finally:
+            phil.release()
 
+    
     def __copy__(self):
         """ Create another object wrapper which points to the same id. 
 
@@ -209,22 +201,46 @@ cdef class ObjectID:
         """
         return self.id
 
+    
     def __str__(self):
-        if self._valid:
-            ref = str(H5Iget_ref(self.id))
-        else:
-            ref = "X"
-
-        if self._locked:
-            lstr = "L"
-        else:
-            lstr = "U"
+        ref = str(H5Iget_ref(self.id)) if self._valid else "X"
+        lck = "L" if self._locked else "U"
 
-        return "%9d [%s] (%s) %s" % (self.id, ref, lstr, self.__class__.__name__)
+        return "%9d [%s] (%s) %s" % (self.id, ref, lck, self.__class__.__name__)
 
     def __repr__(self):
         return self.__str__()
 
+# === HDF5 "H5" API ===========================================================
+
+
+def get_libversion():
+    """ () => TUPLE (major, minor, release)
+
+        Retrieve the HDF5 library version as a 3-tuple.
+    """
+    cdef unsigned int major
+    cdef unsigned int minor
+    cdef unsigned int release
+    cdef herr_t retval
+    
+    H5get_libversion(&major, &minor, &release)
+
+    return (major, minor, release)
+
+
+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()
+
+
 # === Public exception hierarchy ==============================================
 
 class H5Error(Exception):
@@ -420,6 +436,7 @@ cdef class ErrorStackElement:
     cdef readonly unsigned int line
     cdef readonly object desc
 
+    
     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),
@@ -442,6 +459,7 @@ cdef herr_t walk_cb(int n, H5E_error_t *err_desc, void* stack_in):
 
     return 0
 
+
 def error_stack():
     """ () => LIST error_stack
 
@@ -452,6 +470,7 @@ def error_stack():
     H5Ewalk(H5E_WALK_DOWNWARD, walk_cb, <void*>stack)
     return stack
 
+
 def error_string():
     """ () => STRING error_stack
 
@@ -484,6 +503,7 @@ def error_string():
 
     return msg
 
+
 def clear():
     """ ()
 
@@ -491,20 +511,6 @@ def clear():
     """
     H5Eclear()
 
-def get_major(int error):
-    """ (INT error) => STRING description
-
-        Get a description associated with an HDF5 minor error code.
-    """
-    return H5Eget_major(<H5E_major_t>error)
-
-def get_minor(int error):
-    """ (INT error) => STRING description
-
-        Get a description associated with an HDF5 minor error code.
-    """
-    return H5Eget_minor(<H5E_minor_t>error)
-
 # === Automatic exception API =================================================
 
 cdef herr_t extract_cb(int n, H5E_error_t *err_desc, void* data_in):
@@ -520,7 +526,8 @@ cdef herr_t err_callback(void* client_data) with gil:
 
     # Can't use the standard Pyrex raise because then the traceback
     # points here.  MUST be "with gil" as it can be called by nogil HDF5
-    # routines.
+    # 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
@@ -548,34 +555,9 @@ cdef int _disable_exceptions() except -1:
         raise RuntimeError("Failed to unregister HDF5 exception callback.")
     return 0
 
-cdef err_c pause_errors() except? NULL:
-    # Temporarily disable automatic exception handling, and return a cookie
-    # which can later be used to re-enable it.
-    cdef err_c cookie
-    cdef void* whatever
-    cookie = NULL
-
-    if H5Eget_auto(&cookie, &whatever) < 0:
-        raise RuntimeError("Failed to retrieve the current error handler.")
-
-    if H5Eset_auto(NULL, NULL) < 0:
-        raise RuntimeError("Failed to temporarily disable error handling.")
-
-    return cookie
-
-cdef int resume_errors(err_c cookie) except -1:
-    # Resume automatic exception handling, using a cookie from a previous
-    # call to pause_errors().  Also clears the error stack.
-    if H5Eset_auto(cookie, NULL) < 0:
-        raise RuntimeError("Failed to re-enable error handling.")
-    
-    if H5Eclear() < 0:
-        raise RuntimeError("Failed to clear error stack.")
-
-    return 0
-
 # === Library init ============================================================
 
+
 def _exithack():
     """ Internal function; do not call unless you want to lose all your data.
     """
@@ -626,4 +608,3 @@ _config = H5PYConfig()
 
 
 
-
diff --git a/h5py/h5a.pyx b/h5py/h5a.pyx
index 77a3d6c..2c55166 100644
--- a/h5py/h5a.pyx
+++ b/h5py/h5a.pyx
@@ -38,6 +38,7 @@ import_array()
 
 # === General attribute operations ============================================
 
+ at sync
 def create(ObjectID loc not None, char* name, TypeID tid not None, 
             SpaceID space not None):
     """ (ObjectID loc, STRING name, TypeID tid, SpaceID space) 
@@ -48,6 +49,7 @@ def create(ObjectID loc not None, char* name, TypeID tid not None,
     """
     return AttrID(H5Acreate(loc.id, name, tid.id, space.id, H5P_DEFAULT))
 
+ at sync
 def open_idx(ObjectID loc not None, int idx):
     """ (ObjectID loc_id, UINT idx) => INT attr_id
 
@@ -60,6 +62,7 @@ def open_idx(ObjectID loc not None, int idx):
         raise ValueError("Index must be a non-negative integer.")
     return AttrID(H5Aopen_idx(loc.id, idx))
 
+ at sync
 def open_name(ObjectID loc not None, char* name):
     """ (ObjectID loc, STRING name) => INT attr_id
 
@@ -67,6 +70,7 @@ def open_name(ObjectID loc not None, char* name):
     """
     return AttrID(H5Aopen_name(loc.id, name))
 
+ at sync
 def delete(ObjectID loc not None, char* name):
     """ (ObjectID loc, STRING name)
 
@@ -74,6 +78,7 @@ def delete(ObjectID loc not None, char* name):
     """
     H5Adelete(loc.id, name)
 
+ at sync
 def get_num_attrs(ObjectID loc not None):
     """ (ObjectID loc) => INT number_of_attributes
 
@@ -93,6 +98,7 @@ cdef herr_t iter_cb(hid_t loc_id, char *attr_name, object int_tpl) except -1:
 
     return 0
 
+ at sync
 def iterate(ObjectID loc not None, object func, object data=None, int startidx=0):
     """ (ObjectID loc, FUNCTION func, OBJECT data=None, UINT startidx=0)
         => INT last_attribute_index
@@ -126,6 +132,7 @@ cdef herr_t list_cb(hid_t loc_id, char *attr_name, object listin):
     thelist.append(attr_name)
     return 0
 
+ at sync
 def py_listattrs(ObjectID loc not None):
     """ (ObjectID loc) => LIST
 
@@ -144,6 +151,7 @@ cdef herr_t cb_exist(hid_t loc_id, char* attr_name, object ref_name):
         return 1
     return 0
 
+ at sync
 def py_exists(ObjectID loc not None, object ref_name):
     """ (ObjectID loc, STRING ref_name) => BOOL
 
@@ -196,9 +204,10 @@ cdef class AttrID(ObjectID):
         def __get__(self):
 
             cdef TypeID tid
-            tid = typewrap(H5Aget_type(self.id))
+            tid = self.get_type()
             return tid.py_dtype()
 
+    @sync
     def _close(self):
         """ ()
 
@@ -208,6 +217,7 @@ cdef class AttrID(ObjectID):
         """
         H5Aclose(self.id)
 
+    @sync
     def read(self, ndarray arr_obj not None):
         """ (NDARRAY arr_obj)
 
@@ -234,6 +244,7 @@ cdef class AttrID(ObjectID):
             if space_id:
                 H5Sclose(space_id)
 
+    @sync
     def write(self, ndarray arr_obj not None):
         """ (NDARRAY arr_obj)
 
@@ -259,6 +270,7 @@ cdef class AttrID(ObjectID):
             if space_id:
                 H5Sclose(space_id)
 
+    @sync
     def get_name(self):
         """ () => STRING name
 
@@ -279,6 +291,7 @@ cdef class AttrID(ObjectID):
 
         return strout
 
+    @sync
     def get_space(self):
         """ () => INT space_id
 
@@ -286,8 +299,9 @@ cdef class AttrID(ObjectID):
         """
         return SpaceID(H5Aget_space(self.id))
 
+    @sync
     def get_type(self):
-        """ () => INT type_id
+        """ () => TypeID
 
             Create and return a copy of the attribute's datatype.
         """
diff --git a/h5py/h5t.pyx b/h5py/h5t.pyx
index 9ba53fa..7484a3b 100644
--- a/h5py/h5t.pyx
+++ b/h5py/h5t.pyx
@@ -60,7 +60,7 @@
 include "config.pxi"
 
 # Pyrex compile-time imports
-from h5 cimport err_c, pause_errors, resume_errors
+from h5 cimport PHIL, get_phil
 from numpy cimport dtype, ndarray
 from python cimport PyString_FromStringAndSize
 
@@ -72,6 +72,11 @@ from utils cimport  emalloc, efree, pybool, \
 import h5
 import sys
 
+cdef PHIL phil
+phil = get_phil()
+phil.acquire()
+phil.release()
+
 # === Custom C API ============================================================
     
 cdef object typewrap(hid_t id_):
diff --git a/h5py/std_code.pxi b/h5py/std_code.pxi
index 9f794e9..8d80420 100644
--- a/h5py/std_code.pxi
+++ b/h5py/std_code.pxi
@@ -15,19 +15,35 @@
 
 include "config.pxi"
 
-# Set up synchronization decorator for threads
-IF H5PY_THREADS:
-    from _extras import h5sync
-    IF H5PY_DEBUG:
-        import logging
+# Defines the following decorators:
+#
+# sync:     Acquire PHIL for this function, and log function entry and lock
+#           acquisition in debug mode
+# nosync:   Don't acquire PHIL, but log function entry in debug mode.
+#
+
+IF H5PY_DEBUG:
+    import logging
+    from _extras import h5sync_dummy
+    nosync = h5sync_dummy(logging.getLogger('h5py.functions'))
+
+    IF H5PY_THREADS:
+        from _extras import h5sync
         sync = h5sync(logging.getLogger('h5py.functions'))
     ELSE:
-        sync = h5sync()
+        sync = nosync
+
 ELSE:
-    IF H5PY_DEBUG:
-        import logging
-        from _extras import h5sync_dummy
-        sync = h5sync_dummy(logging.getLogger('h5py.functions'))
+    cdef inline object nosync(object func):
+        return func
+
+    IF H5PY_THREADS:
+        from _extras import h5sync
+        sync = h5sync()
     ELSE:
         cdef inline object sync(object func):
             return func
+
+
+
+
diff --git a/setup.py b/setup.py
index 2847481..2b53405 100644
--- a/setup.py
+++ b/setup.py
@@ -15,7 +15,7 @@
 """
     Setup script for the h5py package.  
 
-    All commands take the usual distutils options, like --home, etc.  Pyrex is
+    All commands take the usual distutils options, like --home, etc.  Cython is
     not required for installation, but will be invoked if the .c files are
     missing, one of the --pyrex options is used, or if a non-default API 
     version or debug level is requested.
@@ -32,24 +32,37 @@
     See INSTALL.txt or the h5py manual for additional build options.
 """
 
-# === Global constants ========================================================
+import os
+import sys
+import shutil
+import os.path as op
 
+from distutils.cmd import Command
+from distutils.errors import DistutilsError, DistutilsExecError
+from distutils.core import setup
+from distutils.extension import Extension
+from distutils.command.build import build 
+from distutils.command.clean import clean
+from distutils.command.sdist import sdist
+
+# Basic package options
 NAME = 'h5py'
 VERSION = '0.3.1'
-
-MIN_PYREX = '0.9.8.4'  # for compile_multiple
 MIN_NUMPY = '1.0.3'
+MIN_CYTHON = '0.9.8.1'
+KNOWN_API = (16,18)    # Legal API levels (1.8.X or 1.6.X)
+SRC_PATH = 'h5py'      # Name of directory with .pyx files
+CMD_CLASS = {}         # Custom command classes for setup()
+HDF5 = None            # Custom HDF5 directory.
 
-# === Initial imports and utilities ===========================================
+# The list of modules depends on max API version
+MODULES = {16:  ['h5', 'h5f', 'h5g', 'h5s', 'h5t', 'h5d', 'h5a', 'h5p', 'h5z',
+                 'h5i', 'h5r', 'h5fd', 'utils'],
+           18:  ['h5', 'h5f', 'h5g', 'h5s', 'h5t', 'h5d', 'h5a', 'h5p', 'h5z',
+                 'h5i', 'h5r', 'h5fd', 'utils', 'h5o', 'h5l']}
 
-from distutils.cmd import Command
-from distutils.errors import DistutilsError, DistutilsExecError
-from distutils.core import setup
-from distutils.extension import Extension
-import os
-import sys
-import shutil
-import os.path as op
+# C source files required for all modules
+EXTRA_SRC = ['utils_low.c']
 
 def fatal(instring, code=1):
     print >> sys.stderr, "Fatal: "+instring
@@ -58,82 +71,10 @@ def fatal(instring, code=1):
 def warn(instring):
     print >> sys.stderr, "Warning: "+instring
 
-# === Parse command line arguments ============================================
-
-class CmdOptions(object):
-    pass
-opts = CmdOptions()
-
-opts.AUTO_HDR = "# This file is automatically generated.  Do not edit."
-opts.VERSION = VERSION
-opts.ENABLE_PYREX = False        # Flag: Pyrex must be run
-opts.PYREX_ONLY = False          # Flag: Run Pyrex, but don't perform build
-opts.PYREX_FORCE = False         # Flag: Disable Pyrex timestamp checking
-opts.PYREX_FORCE_OFF = False     # Flag: Don't run Pyrex, no matter what
-
-opts.API_16 = True               # Include 1.6.X-specific functions
-opts.API_18 = False              # Include new 1.8.X functions
-opts.DEBUG_LEVEL = 0             # Logging-module number, 0 to turn off
-opts.HDF5_DIR = None             # Custom HDF5 directory, or None
-opts.IO_NONBLOCK = False         # Experimental non-blocking I/O
-
-for arg in sys.argv[:]:
-    if arg == '--pyrex':
-        opts.ENABLE_PYREX = True
-        sys.argv.remove(arg)
-    elif arg == '--pyrex-only':
-        opts.ENABLE_PYREX = True
-        opts.PYREX_ONLY = True
-        sys.argv.remove(arg)
-    elif arg == '--pyrex-force':
-        opts.ENABLE_PYREX=True
-        opts.PYREX_FORCE = True
-        sys.argv.remove(arg)
-    elif arg == '--no-pyrex':
-        opts.PYREX_FORCE_OFF = True
-        sys.argv.remove(arg)
-    elif arg.find('--api=') == 0:
-        opts.ENABLE_PYREX=True
-        api = arg[6:]
-        api = api.split(',')
-        if not all(x in ("16","18") for x in api):
-            fatal('Illegal option to --api (legal values are "16" and "18")')
-        if not ("16" in api or "18" in api):
-            fatal("At least one of 1.6 or 1.8 compatibility must be specified.")
-        opts.API_16 = "16" in api
-        opts.API_18 = "18" in api
-        sys.argv.remove(arg)
-    elif arg.find('--debug=') == 0:
-        opts.ENABLE_PYREX=True
-        try:
-            opts.DEBUG_LEVEL = int(arg[8:])
-        except:
-            fatal('Debuglevel not understood (wants --debug=<n>)')
-        sys.argv.remove(arg)
-    elif arg.find('--hdf5=') == 0:
-        splitarg = arg.split('=',1)
-        if len(splitarg) != 2:
-            fatal("HDF5 directory not understood (wants --hdf5=/path/to/hdf5)")
-        opts.HDF5_DIR = op.abspath(splitarg[1])
-        sys.argv.remove(arg)
-    elif arg.find('--io-nonblock') == 0:
-        opts.ENABLE_PYREX=True
-        opts.IO_NONBLOCK = True
-        sys.argv.remove(arg)
-
-opts.API_MAJ = 1
-opts.API_MIN = 8 if opts.API_18 else 6
-
-if 'sdist' in sys.argv:
-    if os.path.exists('MANIFEST'):
-        warn("Cleaning up stale MANIFEST file")
-        os.remove('MANIFEST')
-    shutil.copyfile(reduce(op.join, ('docs', 'source', 'build.rst')), 'INSTALL.txt')
-
 # === Required imports ========================================================
 
 # Check Python version (2.5 or greater required)
-if not (sys.version_info[0] == 2 and sys.version_info[1] >= 5):
+if not (sys.version_info[0:2] >= (2,5)):
     fatal("At least Python 2.5 is required to install h5py")
 
 # Check for Numpy (required)
@@ -141,51 +82,46 @@ try:
     import numpy
     if numpy.version.version < MIN_NUMPY:
         fatal("Numpy version %s is out of date (>= %s needed)" % (numpy.version.version, MIN_NUMPY))
-
 except ImportError:
     fatal("Numpy not installed (version >= %s required)" % MIN_NUMPY)
-        
-# === Platform configuration & Pyrex check ====================================
-
-# Pyrex modules (without extension)
-modules = ['h5' , 'h5f', 'h5g', 'h5s', 'h5t', 'h5d',
-           'h5a', 'h5p', 'h5z', 'h5i', 'h5r', 'h5fd', 'utils']
-
-if (opts.API_MAJ, opts.API_MIN) >= (1,8):
-    modules += ['h5o','h5l']
 
-# C source files required for Pyrex code (with extension)
-extra_src = ['utils_low.c']    
-
-# Where these live, relative to setup.py
-src_path = 'h5py'
+for arg in sys.argv[:]:
+    if arg.find('--hdf5=') == 0:
+        splitarg = arg.split('=',1)
+        if len(splitarg) != 2:
+            fatal("HDF5 directory not understood (wants --hdf5=/path/to/hdf5)")
+        path = op.abspath(splitarg[1])
+        if not op.exists(path):
+            fatal("HDF5 path is illegal: %s" % path)
+        HDF5 = path
+        sys.argv.remove(arg)
+        
+# === Platform-dependent compiler config ======================================
 
-# Platform-dependent arguments to setup() or Extension()
 if os.name == 'nt':
-
-    if opts.HDF5_DIR is None:
+    if HDF5 is None:
         fatal("On Windows, HDF5 directory must be specified.")
 
     libraries = ['hdf5dll']
-    include_dirs = [numpy.get_include(), op.join(opts.HDF5_DIR, 'include')]
-    library_dirs = [op.join(opts.HDF5_DIR, 'dll2')]  # Must contain only "hdf5dll.dll.a"
+    include_dirs = [numpy.get_include(), op.join(HDF5, 'include')]
+    library_dirs = [op.join(HDF5, 'dll2')]  # Must contain only "hdf5dll.dll.a"
     runtime_dirs = []
     extra_compile_args = ['-DH5_USE_16_API', '-D_HDF5USEDLL_', '-DH5_SIZEOF_SSIZE_T=4']
     extra_link_args = []
     package_data = {'h5py': ['*.pyx', '*.dll', 
-                                'Microsoft.VC90.CRT/*.manifest',
-                                'Microsoft.VC90.CRT/*.dll'],
-                   'h5py.tests': ['data/*.hdf5']}
+                                    'Microsoft.VC90.CRT/*.manifest',
+                                    'Microsoft.VC90.CRT/*.dll'],
+                       'h5py.tests': ['data/*.hdf5']}
 
-else:
+else:   # Assume Unix-like
 
     libraries = ['hdf5']
-    if opts.HDF5_DIR is None:
+    if HDF5 is None:
         include_dirs = [numpy.get_include(), '/usr/include', '/usr/local/include']
         library_dirs = ['/usr/lib/', '/usr/local/lib']
     else:
-        include_dirs = [numpy.get_include(), op.join(opts.HDF5_DIR, 'include')]
-        library_dirs = [op.join(opts.HDF5_DIR, 'lib')]
+        include_dirs = [numpy.get_include(), op.join(HDF5, 'include')]
+        library_dirs = [op.join(HDF5, 'lib')]
     runtime_dirs = library_dirs
     extra_compile_args = ['-DH5_USE_16_API', '-Wno-unused', '-Wno-uninitialized']
     extra_link_args = []
@@ -193,177 +129,263 @@ else:
     package_data = {'h5py': ['*.pyx'],
                    'h5py.tests': ['data/*.hdf5']}
 
-# If for some reason the .c files are missing, Pyrex is required.
-cfiles = [op.join(src_path, x+'.c') for x in modules]
+# The actual extensions themselves are created at runtime, as the list of
+# modules depends on command-line options.
+def create_extension(name):
+    """ Create a distutils Extension object for the given module.  Uses the
+        globals in this file for things like the include directory.
+    """
+    sources = [op.join(SRC_PATH, name+'.c')]+[op.join(SRC_PATH,x) for x in EXTRA_SRC]
+    ext = Extension(NAME+'.'+name,
+                        sources, 
+                        include_dirs = include_dirs, 
+                        libraries = libraries,
+                        library_dirs = library_dirs,
+                        runtime_library_dirs = runtime_dirs,
+                        extra_compile_args = extra_compile_args,
+                        extra_link_args = extra_link_args)
+    return ext
 
-if not all( [op.exists(x) for x in cfiles]):
-    opts.ENABLE_PYREX = True
 
-if opts.ENABLE_PYREX and not opts.PYREX_FORCE_OFF:
-    print "Running Pyrex..."
+# === Custom extensions for distutils =========================================
 
-    try:
-        from Pyrex.Compiler.Main import Version
+class cybuild(build):
 
-        if Version.version >= MIN_PYREX:
-            from Pyrex.Compiler.Main import compile_multiple, CompilationOptions
+    """ Cython-aware subclass of the distutils build command
 
-            # Check if the conditions.pxi file is up-to-date
-            cond_path = op.join(src_path, 'conditions.pxi')
-            cond = \
-"""
-%(AUTO_HDR)s
+        It handles the h5py-centric configuration for the build processing,
+        generating the compile-time definitions file "config.pxi" and
+        running Cython before handing control over to the native distutils
+        build.  The advantage is that Cython is run on all files before
+        building begins, and is only run when the distribution is actually
+        being built (and not, for example, when "sdist" is called for).
+    """
 
-DEF H5PY_VERSION = "%(VERSION)s"
-DEF H5PY_API_MAJ = %(API_MAJ)d
-DEF H5PY_API_MIN = %(API_MIN)d
-DEF H5PY_DEBUG = %(DEBUG_LEVEL)d
+    user_options = build.user_options + \
+                    [('cython','y','Run Cython'),
+                     ('cython-only','Y', 'Run Cython and stop'),
+                     ('diag', 'd','Enable library logging'),
+                     ('api=', 'a', 'Set API levels (--api=16,18)'),
+                     ('threads', 't', 'Thread-aware')]
+    boolean_options = build.boolean_options + ['cython', 'threads','diag']
 
-DEF H5PY_16API = %(API_16)d
-DEF H5PY_18API = %(API_18)d
+    def initialize_options(self):
+        self.cython = False
+        self.cython_only = False
+        self.threads = False
+        self.api = (16,)
+        self.diag = False
+        self._explicit_only = False     # Hack for test subclass
+        build.initialize_options(self)
+
+    def finalize_options(self):
 
-DEF H5PY_NONBLOCK = %(IO_NONBLOCK)d
-""" \
-            % opts.__dict__
+        if self.cython_only or  \
+           self.api != (16,) or \
+           self.threads or \
+           self.diag:
+            self.cython = True
+
+        # Validate API levels
+        if self.api != (16,):
             try:
-                cond_file = open(cond_path,'r')
-                cond_present = cond_file.read()
-                cond_file.close()
-            except IOError:
-                cond_present = ""
-
-            # If we regenerate the file every time, Pyrex's timestamp checking
-            # is useless.  So only replace it if it's out of date.
-            if cond_present != cond:
-                print "Replacing conditions file..."
-                print cond
-                cond_file = open(cond_path,'w')
-                cond_file.write(cond)
-                cond_file.close()
-
-            pyxopts = CompilationOptions(verbose=True, timestamps=(not opts.PYREX_FORCE))
-            results = compile_multiple( [op.join(src_path,x+'.pyx') for x in modules], pyxopts)
-
-            if results.num_errors != 0:
-                fatal("%d Pyrex compilation errors encountered; aborting." % results.num_errors)
-            if opts.PYREX_ONLY:
-                exit(0)
-        else:
-            fatal("Old Pyrex version %s detected (min %s)" % (Version.version, MIN_PYREX))
+                self.api = tuple(int(x) for x in self.api.split(',') if len(x) > 0)
+                if len(self.api) == 0 or not all(x in KNOWN_API for x in self.api):
+                    raise Exception
+            except Exception:
+                fatal('Illegal option %s to --api= (legal values are %s)' % (self.api, ','.join(str(x) for x in KNOWN_API)))
 
-    except ImportError:
-        fatal("Pyrex recompilation required, but Pyrex not installed.")
-else:
-    pass
-    #print "Skipping Pyrex..."
-
-# One extension is built for each module
-extensions = []
-for module in modules:
-    mod_sources  = [op.join(src_path, module) +'.c']
-    mod_sources += [op.join(src_path, x) for x in extra_src]
-
-    extensions.append(
-        Extension( 
-            NAME+'.'+module,
-            mod_sources, 
-            include_dirs = include_dirs, 
-            libraries = libraries,
-            library_dirs = library_dirs,
-            runtime_library_dirs = runtime_dirs,
-            extra_compile_args = extra_compile_args,
-            extra_link_args = extra_link_args
-        )
-    )
+        build.finalize_options(self)
 
-# === Custom extensions for distutils =========================================
+    def _get_pxi(self):
+        """ Get the configuration .pxi for the current options. """
+
+        pxi_str = \
+"""# This file is automatically generated.  Do not edit.
+# HDF5: %(HDF5)s
 
-class test(Command):
-    description = "Build %s and run unit tests" % NAME
-    user_options = [('sections=','s','Comma separated list of tests ("-" prefix to NOT run)')]
+DEF H5PY_VERSION = "%(VERSION)s"
+
+DEF H5PY_API = %(API_MAX)d     # Highest API level (i.e. 18 or 16)
+DEF H5PY_16API = %(API_16)d    # 1.6.X API available
+DEF H5PY_18API = %(API_18)d    # 1.8.X API available
+
+DEF H5PY_DEBUG = %(DEBUG)d    # Logging-level number, or 0 to disable
+
+DEF H5PY_THREADS = %(THREADS)d  # Enable thread-safety and non-blocking reads
+"""
+        pxi_str %= {"VERSION": VERSION, "API_MAX": max(self.api),
+                    "API_16": 16 in self.api, "API_18": 18 in self.api,
+                    "DEBUG": 10 if self.diag else 0, "THREADS": self.threads,
+                    "HDF5": HDF5}
+
+        return pxi_str
+
+    def run(self, *args, **kwds):
+
+        modules = MODULES[max(self.api)]
+        self.distribution.ext_modules = [create_extension(x) for x in modules]
+
+        # Necessary because Cython doesn't detect changes to the .pxi
+        recompile_all = False
+
+        # Check if the config.pxi file needs to be updated for the given
+        # command-line options.
+        if self.cython or not self._explicit_only:
+            pxi_path = op.join(SRC_PATH, 'config.pxi')
+            pxi = self._get_pxi()
+            if not op.exists(pxi_path):
+                try:
+                    f = open(pxi_path, 'w')
+                    f.write(pxi)
+                    f.close()
+                except IOError:
+                    fatal('Failed write to "%s"' % pxi_path)
+                recompile_all = True
+            else:
+                try:
+                    f = open(pxi_path, 'r+')
+                except IOError:
+                    fatal("Can't read file %s" % pxi_path)
+                if f.read() != pxi:
+                    f.close()
+                    f = open(pxi_path, 'w')
+                    f.write(pxi)
+                    recompile_all = True
+                f.close()
+
+        if self.cython or recompile_all:
+            print "Running Cython..."
+            try:
+                from Cython.Compiler.Main import Version, compile, CompilationOptions
+                from Cython.Distutils import build_ext
+            except ImportError:
+                fatal("Cython recompilation required, but Cython not installed.")
+
+            if Version.version < MIN_CYTHON:
+                fatal("Old Cython version detected; at least %s required" % MIN_CYTHON)
+
+            print "  API levels: %s" % ','.join(str(x) for x in self.api)
+            print "  Thread-aware: %s" % ('yes' if self.threads else 'no')
+            print "  Diagnostic mode: %s" % ('yes' if self.diag else 'no')
+            print "  HDF5: %s" % ('default' if HDF5 is None else HDF5)
+
+            cyopts = CompilationOptions(verbose=False)
+
+            # Build each extension
+            # This should be a single call to compile_multiple, but it's
+            # broken in Cython 0.9.8.1.1
+            for module in modules:
+                pyx_path = op.join(SRC_PATH,module+'.pyx')
+                c_path = op.join(SRC_PATH,module+'.c')
+                if not op.exists(c_path) or \
+                   os.stat(pyx_path).st_mtime > os.stat(c_path).st_mtime or \
+                   recompile_all or\
+                   self.force:
+                    print "Cythoning %s" % pyx_path
+                    result = compile(pyx_path, cyopts)
+                    if result.num_errors != 0:
+                        fatal("Cython error; aborting.")
+
+        if self.cython_only:
+            exit(0)
+
+        build.run(self, *args, **kwds)
+
+class test(cybuild):
+    description = "Build and run unit tests"
+    user_options = cybuild.user_options + \
+                   [('sections=','s','Comma separated list of tests ("-" prefix to NOT run)')]
 
     def initialize_options(self):
         self.sections = None
+        cybuild.initialize_options(self)
 
     def finalize_options(self):
         pass
+        cybuild.finalize_options(self)
 
     def run(self):
-        buildobj = self.distribution.get_command_obj('build')
-        buildobj.run()
+        self._explicit_only = True
+        cybuild.run(self)
         oldpath = sys.path
         try:
-            sys.path = [op.abspath(buildobj.build_lib)] + oldpath
+            sys.path = [op.abspath(self.build_lib)] + oldpath
             import h5py.tests
             if not h5py.tests.runtests(None if self.sections is None else tuple(self.sections.split(','))):
                 raise DistutilsError("Unit tests failed.")
         finally:
             sys.path = oldpath
 
-class dev(Command):
+class doc(cybuild):
 
-    description = "Developer commands (--doc, --clean, --readme=<file>)"
-    user_options = [('doc','d','Rebuild documentation'),
-                    ('clean', 'c', 'Remove built files and Pyrex temp files.')]
-    boolean_options = ['doc']
+    """ Regenerate documentation.  Unix only, requires epydoc/sphinx. """
 
-    def initialize_options(self):
-        self.doc = False
-        self.readme = False
-        self.clean = False
+    description = "Rebuild documentation"
 
-    def finalize_options(self):
-        pass
+    def run(self):
+
+        cybuild.run(self)
+
+        for x in ('docs', 'docs/api-html'):
+            if not op.exists(x):
+                os.mkdir(x, 0755)
+
+        retval = os.spawnlp(os.P_WAIT, 'epydoc', '-q', '--html',
+                    '-o', 'docs/api-html', '--config', 'docs.cfg', 
+                    os.path.join(self.build_lib, NAME) )
+        if retval != 0:
+            warn("Could not run epydoc to build documentation.")
+
+
+        retval = os.system("cd docs; make html")
+        if retval != 0:
+            warn("Could not run Sphinx doc generator")
+        else:
+            if op.exists('docs/manual-html'):
+                shutil.rmtree('docs/manual-html')
+            shutil.copytree('docs/build/html', 'docs/manual-html')
+
+class cyclean(clean):
 
     def run(self):
-        if self.clean:
-            for x in ('build','docs/api-html', 'docs/manual-html'):
-                try:
-                    shutil.rmtree(x)
-                except OSError:
-                    pass
-            fnames = [ op.join(src_path, x+'.dep') for x in modules ] + \
-                     [ op.join(src_path, x+'.c') for x in modules ] + \
-                     [ 'MANIFEST']
-
-            for name in fnames:
-                try:
-                    os.remove(name)
-                except OSError:
-                    pass
-
-        if self.doc:
-            buildobj = self.distribution.get_command_obj('build')
-            buildobj.run()
-            for x in ('docs', 'docs/api-html'):
-                if not op.exists(x):
-                    os.mkdir(x, 0755)
-
-            retval = os.spawnlp(os.P_WAIT, 'epydoc', '-q', '--html',
-                        '-o', 'docs/api-html', '--config', 'docs.cfg', 
-                        os.path.join(buildobj.build_lib, NAME) )
-            if retval != 0:
-                warn("Could not run epydoc to build documentation.")
-
-
-            retval = os.system("cd docs; make html")
-            if retval != 0:
-                warn("Could not run Sphinx doc generator")
-            else:
-                if op.exists('docs/manual-html'):
-                    shutil.rmtree('docs/manual-html')
-                shutil.copytree('docs/build/html', 'docs/manual-html')
+        
+        allmodules = set()
+        for x in MODULES.values():
+            allmodules.update(x)
+
+        for x in ('build','docs/api-html', 'docs/manual-html'):
+            try:
+                shutil.rmtree(x)
+            except OSError:
+                pass
+        fnames = [ op.join(SRC_PATH, x+'.dep') for x in allmodules ] + \
+                 [ op.join(SRC_PATH, x+'.c') for x in allmodules ] + \
+                 [ 'MANIFEST']
+
+        for name in fnames:
+            try:
+                os.remove(name)
+            except OSError:
+                pass
+
+        clean.run(self)
+
+class new_sdist(sdist):
+
+    def run(self):
+        if os.path.exists('MANIFEST'):
+            os.remove('MANIFEST')
+        shutil.copyfile(reduce(op.join, ('docs', 'source', 'build.rst')), 'INSTALL.txt')
+
+        sdist.run(self)
 
 # New commands for setup (e.g. "python setup.py test")
 if os.name == 'nt':
-    CMD_CLASS = {'test': test}
+    CMD_CLASS.update({'build': cybuild, 'test': test, 'sdist': new_sdist})
 else:
-    CMD_CLASS = {'dev': dev, 'test': test}
-
-#print "Configuration"
-#print '-'*40
-#for key in sorted(opts.__dict__):
-#    print "%-20s %s" % (key, opts.__dict__[key])
+    CMD_CLASS.update({'build': cybuild, 'test': test, 'sdist': new_sdist,
+                      'doc': doc, 'clean': cyclean, })
 
 
 cls_txt = \
@@ -409,8 +431,8 @@ setup(
   url = 'h5py.alfven.org',
   packages = ['h5py','h5py.tests'],
   package_data = package_data,
-  ext_modules = extensions,
-  requires = ['numpy (>=1.0.3)'],
+  ext_modules = [],
+  requires = ['numpy (>=%s)' % MIN_NUMPY],
   cmdclass = CMD_CLASS
 )
 
diff --git a/setup_cython.py b/setup_cython.py
deleted file mode 100644
index a49d8dd..0000000
--- a/setup_cython.py
+++ /dev/null
@@ -1,430 +0,0 @@
-#!/usr/bin/env python
-
-#+
-# 
-# This file is part of h5py, a low-level Python interface to the HDF5 library.
-# 
-# Copyright (C) 2008 Andrew Collette
-# http://h5py.alfven.org
-# License: BSD  (See LICENSE.txt for full license)
-# 
-# $Date$
-# 
-#-
-
-"""
-    Setup script for the h5py package.  
-
-    All commands take the usual distutils options, like --home, etc.  Cython is
-    not required for installation, but will be invoked if the .c files are
-    missing, one of the --pyrex options is used, or if a non-default API 
-    version or debug level is requested.
-
-    To build:
-    python setup.py build
-
-    To install:
-    sudo python setup.py install
-
-    To run the test suite locally (won't install anything):
-    python setup.py test
-
-    See INSTALL.txt or the h5py manual for additional build options.
-"""
-
-import os
-import sys
-import shutil
-import os.path as op
-
-from distutils.cmd import Command
-from distutils.errors import DistutilsError, DistutilsExecError
-from distutils.core import setup
-from distutils.extension import Extension
-from distutils.command.build import build 
-
-
-# Basic package options
-NAME = 'h5py'
-VERSION = '0.3.1'
-MIN_NUMPY = '1.0.3'
-MIN_CYTHON = '0.9.8.1'
-KNOWN_API = (16,18)    # Legal API levels (1.8.X or 1.6.X)
-SRC_PATH = 'h5py'      # Name of directory with .pyx files
-CMD_CLASS = {}         # Custom command classes for setup()
-
-# Compilation flags
-HDF5 = None            # Custom HDF5 directory.
-API = "16"
-
-def fatal(instring, code=1):
-    print >> sys.stderr, "Fatal: "+instring
-    exit(code)
-
-def warn(instring):
-    print >> sys.stderr, "Warning: "+instring
-
-# === Required imports ========================================================
-
-# Check Python version (2.5 or greater required)
-if not (sys.version_info[0:2] >= (2,5)):
-    fatal("At least Python 2.5 is required to install h5py")
-
-# Check for Numpy (required)
-try:
-    import numpy
-    if numpy.version.version < MIN_NUMPY:
-        fatal("Numpy version %s is out of date (>= %s needed)" % (numpy.version.version, MIN_NUMPY))
-
-except ImportError:
-    fatal("Numpy not installed (version >= %s required)" % MIN_NUMPY)
-
-# === Parse command line arguments ============================================
-
-for arg in sys.argv[:]:
-
-    if arg.find('--hdf5=') == 0:
-        splitarg = arg.split('=',1)
-        if len(splitarg) != 2:
-            fatal("HDF5 directory not understood (wants --hdf5=/path/to/hdf5)")
-        path = op.abspath(splitarg[1])
-        if not op.exists(path):
-            fatal("HDF5 path is illegal: %s" % path)
-        HDF5 = path
-        sys.argv.remove(arg)
-
-    if arg.find('--api=') == 0:
-        # We need to know this in order to generate the correct module list,
-        # although it's technically handled by the build object.
-        API = arg[5:]
-
-if 'sdist' in sys.argv:
-    if os.path.exists('MANIFEST'):
-        warn("Cleaning up stale MANIFEST file")
-        os.remove('MANIFEST')
-    shutil.copyfile(reduce(op.join, ('docs', 'source', 'build.rst')), 'INSTALL.txt')
-        
-# === Platform configuration & Cython check ====================================
-
-# Cython modules (without extension)
-modules = ['h5' , 'h5f', 'h5g', 'h5s', 'h5t', 'h5d',
-                'h5a', 'h5p', 'h5z', 'h5i', 'h5r', 'h5fd', 'utils']
-
-# Only enable H5O and H5L interface if we're building against 1.8.X
-if "18" in API:
-    modules += ['h5o','h5l']
-
-# C source files required for Cython code (with extension)
-extra_src = ['utils_low.c']    
-
-
-# Platform-dependent arguments to setup() or Extension()
-if os.name == 'nt':
-    if HDF5 is None:
-        fatal("On Windows, HDF5 directory must be specified.")
-
-    libraries = ['hdf5dll']
-    include_dirs = [numpy.get_include(), op.join(HDF5, 'include')]
-    library_dirs = [op.join(HDF5, 'dll2')]  # Must contain only "hdf5dll.dll.a"
-    runtime_dirs = []
-    extra_compile_args = ['-DH5_USE_16_API', '-D_HDF5USEDLL_', '-DH5_SIZEOF_SSIZE_T=4']
-    extra_link_args = []
-    package_data = {'h5py': ['*.pyx', '*.dll', 
-                                    'Microsoft.VC90.CRT/*.manifest',
-                                    'Microsoft.VC90.CRT/*.dll'],
-                       'h5py.tests': ['data/*.hdf5']}
-
-else:   # Assume Unix-like
-
-    libraries = ['hdf5']
-    if HDF5 is None:
-        include_dirs = [numpy.get_include(), '/usr/include', '/usr/local/include']
-        library_dirs = ['/usr/lib/', '/usr/local/lib']
-    else:
-        include_dirs = [numpy.get_include(), op.join(HDF5, 'include')]
-        library_dirs = [op.join(HDF5, 'lib')]
-    runtime_dirs = library_dirs
-    extra_compile_args = ['-DH5_USE_16_API', '-Wno-unused', '-Wno-uninitialized']
-    extra_link_args = []
-
-    package_data = {'h5py': ['*.pyx'],
-                   'h5py.tests': ['data/*.hdf5']}
-
-# Explicit list of C source files for each module.
-mod_paths = [op.join(SRC_PATH, x) for x in modules]
-extra_paths = [op.join(SRC_PATH, x) for x in extra_src]
-module_sources = [[x+'.c']+extra_paths for x in mod_paths]
-
-extensions = [ Extension(NAME+'.'+module,
-                        sources, 
-                        include_dirs = include_dirs, 
-                        libraries = libraries,
-                        library_dirs = library_dirs,
-                        runtime_library_dirs = runtime_dirs,
-                        extra_compile_args = extra_compile_args,
-                        extra_link_args = extra_link_args)
-                for module, sources in zip(modules, module_sources) ]
-
-
-# === Custom extensions for distutils =========================================
-
-class cybuild(build):
-
-    """ Cython-aware subclass of build """
-
-    user_options = build.user_options + \
-                    [('cython','c','Run Cython'),
-                     ('hdebug=', 'q','Set debug level'),
-                     ('api=', 'a', 'API levels'),
-                     ('threads', 't', 'Thread-aware')]
-    boolean_options = build.boolean_options + ['cython', 'threads']
-
-    def initialize_options(self):
-        self.cython = False
-        self.threads = False
-        self.api = (16,)
-        self.hdebug = 0
-        build.initialize_options(self)
-
-    def finalize_options(self):
-
-        print "finalizing"
-        # Validate API levels
-        if self.api != (16,):
-            self.cython = True
-            print self.api
-            try:
-                self.api = tuple(int(x) for x in self.api.split(',') if len(x) > 0)
-                if len(self.api) == 0 or not all(x in KNOWN_API for x in self.api):
-                    raise Exception
-            except Exception:
-                fatal('Illegal option %s to --api= (legal values are %s)' % (self.api, ','.join(str(x) for x in KNOWN_API)))
-
-        # Validate debug level
-        if self.hdebug != 0:
-            print self.hdebug
-            self.cython = True
-            try:
-                self.hdebug = int(self.hdebug)
-            except TypeError:
-                fatal('Debuglevel not understood (wants --debug=<integer>)')
-        build.finalize_options(self)
-
-    def _get_pxi(self):
-        """ Get the configuration .pxi for the current options. """
-
-        pxi_str = \
-"""# This file is automatically generated.  Do not edit.
-# HDF5: %(HDF5)s
-
-DEF H5PY_VERSION = "%(VERSION)s"
-
-DEF H5PY_API = %(API_MAX)d     # Highest API level (i.e. 18 or 16)
-DEF H5PY_16API = %(API_16)d    # 1.6.X API available
-DEF H5PY_18API = %(API_18)d    # 1.8.X API available
-
-DEF H5PY_DEBUG = %(DEBUG)d    # Logging-level number, or 0 to disable
-
-DEF H5PY_THREADS = %(THREADS)d  # Enable thread-safety and non-blocking reads
-"""
-        pxi_str %= {"VERSION": VERSION, "API_MAX": max(self.api),
-                    "API_16": 16 in self.api, "API_18": 18 in self.api,
-                    "DEBUG": self.hdebug, "THREADS": self.threads,
-                    "HDF5": HDF5}
-
-        return pxi_str
-
-    def run(self, *args, **kwds):
-
-        # Necessary because Cython doesn't detect changes to the .pxi
-        recompile_all = False
-
-        # Check if the config.pxi file needs to be updated for the given
-        # command-line options.
-        pxi_path = op.join(SRC_PATH, 'config.pxi')
-        pxi = self._get_pxi()
-        if not op.exists(pxi_path):
-            try:
-                f = open(pxi_path, 'w')
-                f.write(pxi)
-                f.close()
-            except IOError:
-                fatal('Failed write to "%s"' % pxi_path)
-            recompile_all = True
-        else:
-            try:
-                f = open(pxi_path, 'r+')
-            except IOError:
-                fatal("Can't read file %s" % pxi_path)
-            if f.read() != pxi:
-                f.close()
-                f = open(pxi_path, 'w')
-                f.write(pxi)
-                recompile_all = True
-            f.close()
-
-        if self.force: forceall = True
-
-        if self.cython:
-            print "Running Cython..."
-            try:
-                from Cython.Compiler.Main import Version, compile, CompilationOptions
-                from Cython.Distutils import build_ext
-            except ImportError:
-                fatal("Cython recompilation required, but Cython not installed.")
-
-            if Version.version < MIN_CYTHON:
-                fatal("Old Cython version detected; at least %s required" % MIN_CYTHON)
-
-            cyopts = CompilationOptions(verbose=False)
-
-            # Build each extension
-            # This should be a single call to compile_multiple, but it's
-            # broken in Cython 0.9.8.1.1
-            for module in modules:
-                pyx_path = op.join(SRC_PATH,module+'.pyx')
-                c_path = op.join(SRC_PATH,module+'.c')
-                if not op.exists(c_path) or \
-                   os.stat(pyx_path).st_mtime > os.stat(c_path).st_mtime or \
-                   recompile_all:
-                    print "Cythoning %s" % pyx_path
-                    result = compile(pyx_path, cyopts)
-                    if result.num_errors != 0:
-                        fatal("Cython error; aborting.")
-
-        build.run(self, *args, **kwds)
-
-class test(cybuild):
-    description = "Build and run unit tests"
-    user_options = cybuild.user_options + [('sections=','s','Comma separated list of tests ("-" prefix to NOT run)')]
-
-    def initialize_options(self):
-        self.sections = None
-        cybuild.initialize_options(self)
-
-    def finalize_options(self):
-        pass
-        cybuild.finalize_options(self)
-
-    def run(self):
-        cybuild.run(self)
-        oldpath = sys.path
-        try:
-            sys.path = [op.abspath(self.build_lib)] + oldpath
-            import h5py.tests
-            if not h5py.tests.runtests(None if self.sections is None else tuple(self.sections.split(','))):
-                raise DistutilsError("Unit tests failed.")
-        finally:
-            sys.path = oldpath
-
-class dev(Command):
-
-    description = "Developer commands (--doc, --clean, --readme=<file>)"
-    user_options = [('doc','d','Rebuild documentation'),
-                    ('clean', 'c', 'Remove built files and Cython temp files.')]
-    boolean_options = ['doc']
-
-    def initialize_options(self):
-        self.doc = False
-        self.readme = False
-        self.clean = False
-
-    def finalize_options(self):
-        pass
-
-    def run(self):
-        if self.clean:
-            for x in ('build','docs/api-html', 'docs/manual-html'):
-                try:
-                    shutil.rmtree(x)
-                except OSError:
-                    pass
-            fnames = [ op.join(SRC_PATH, x+'.dep') for x in modules ] + \
-                     [ op.join(SRC_PATH, x+'.c') for x in modules ] + \
-                     [ 'MANIFEST']
-
-            for name in fnames:
-                try:
-                    os.remove(name)
-                except OSError:
-                    pass
-
-        if self.doc:
-            buildobj = self.distribution.get_command_obj('build')
-            buildobj.run()
-            for x in ('docs', 'docs/api-html'):
-                if not op.exists(x):
-                    os.mkdir(x, 0755)
-
-            retval = os.spawnlp(os.P_WAIT, 'epydoc', '-q', '--html',
-                        '-o', 'docs/api-html', '--config', 'docs.cfg', 
-                        os.path.join(buildobj.build_lib, NAME) )
-            if retval != 0:
-                warn("Could not run epydoc to build documentation.")
-
-
-            retval = os.system("cd docs; make html")
-            if retval != 0:
-                warn("Could not run Sphinx doc generator")
-            else:
-                if op.exists('docs/manual-html'):
-                    shutil.rmtree('docs/manual-html')
-                shutil.copytree('docs/build/html', 'docs/manual-html')
-
-# New commands for setup (e.g. "python setup.py test")
-if os.name == 'nt':
-    CMD_CLASS.update({'build': cybuild, 'test': test})
-else:
-    CMD_CLASS.update({'build': cybuild, 'dev': dev, 'test': test})
-
-
-cls_txt = \
-"""
-Development Status :: 4 - Beta
-Intended Audience :: Developers
-Intended Audience :: Information Technology
-Intended Audience :: Science/Research
-License :: OSI Approved :: BSD License
-Programming Language :: Python
-Topic :: Scientific/Engineering
-Topic :: Software Development :: Libraries :: Python Modules
-Operating System :: Unix
-Operating System :: POSIX :: Linux
-Operating System :: MacOS :: MacOS X
-Operating System :: Microsoft :: Windows
-"""
-
-short_desc = "General-purpose Python bindings for the HDF5 library"
-
-long_desc = \
-"""
-The h5py package provides both a high- and low-level interface to the HDF5
-library from Python. The low-level interface is intended to be a complete
-wrapping of the HDF5 API, while the high-level component supports Python-style
-object-oriented access to HDF5 files, datasets and groups.
-
-A strong emphasis on automatic conversion between Python (Numpy) datatypes and
-data structures and their HDF5 equivalents vastly simplifies the process of
-reading and writing data from Python. 
-"""
-
-setup(
-  name = NAME,
-  version = VERSION,
-  description = short_desc,
-  long_description = long_desc,
-  classifiers = [x for x in cls_txt.split("\n") if x],
-  author = 'Andrew Collette',
-  author_email = '"h5py" at the domain "alfven.org"',
-  maintainer = 'Andrew Collette',
-  maintainer_email = '"h5py" at the domain "alfven.org"',
-  url = 'h5py.alfven.org',
-  packages = ['h5py','h5py.tests'],
-  package_data = package_data,
-  ext_modules = extensions,
-  requires = ['numpy (>=%s)' % MIN_NUMPY],
-  cmdclass = CMD_CLASS
-)
-
-
-

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