[h5py] 121/455: More reasonable setup script for Cython

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 8ea25630c765efaf5dc30a020976382faf563d2d
Author: andrewcollette <andrew.collette at gmail.com>
Date:   Sun Sep 21 20:10:09 2008 +0000

    More reasonable setup script for Cython
---
 h5py/__init__.py           |   1 -
 h5py/tests/test_threads.py |  54 --------
 setup_cython.py            | 323 ++++++++++++++++++++++++---------------------
 3 files changed, 173 insertions(+), 205 deletions(-)

diff --git a/h5py/__init__.py b/h5py/__init__.py
index 92a691a..5d5eec0 100644
--- a/h5py/__init__.py
+++ b/h5py/__init__.py
@@ -24,7 +24,6 @@ __doc__ = \
 
 from h5 import _config as config
 import utils, h5, h5a, h5d, h5f, h5g, h5i, h5p, h5r, h5s, h5t, h5z, highlevel
-import extras
 
 from highlevel import File, Group, Dataset, Datatype, AttributeManager, CoordsList
 
diff --git a/h5py/tests/test_threads.py b/h5py/tests/test_threads.py
index 593cb77..6d23795 100644
--- a/h5py/tests/test_threads.py
+++ b/h5py/tests/test_threads.py
@@ -22,7 +22,6 @@ import time
 
 from h5py import *
 from h5py.h5 import H5Error
-from h5py.extras import h5sync
 import h5py
 
 LOCKTYPE = threading.RLock
@@ -212,59 +211,6 @@ class TestThreads(unittest.TestCase):
             self.assert_(writethread.timestop < exit_lock_time)
 
 
-    def test_decorator(self):
-
-        time1 = 0
-        time2 = 0
-
-        class SleeperThread(Thread):
-
-            def __init__(self, sleeptime, next_thread):
-                Thread.__init__(self)
-                self.sleeptime = sleeptime
-                self.time = 0
-                self.next_thread = next_thread
-
-            @h5sync
-            def run(self):
-                if self.next_thread is not None:
-                    self.next_thread.start()  # We should already hold the lock
-                time.sleep(self.sleeptime/2.0)
-                self.time = time.time()
-                time.sleep(self.sleeptime/2.0)
-
-        def run_dec(real_lock):
-            oldlock = h5py.config.lock
-            try:
-                if not real_lock:
-                    h5py.config.lock = dummy_threading.RLock()
-
-                thread_b = SleeperThread(1, None)       # last thread
-                thread_a = SleeperThread(2, thread_b)   # first thread
-
-                thread_a.start()
-
-                thread_a.join()
-                thread_b.join()
-
-                if real_lock:
-                    self.assert_(thread_a.time < thread_b.time, "%f !< %f" % (thread_a.time, thread_b.time))
-                else:
-                    self.assert_(thread_a.time > thread_b.time, "%f !> %f" % (thread_a.time, thread_b.time))
-            finally:
-                h5py.config.lock = oldlock
-
-        run_dec(True)
-        run_dec(False)
-
-        @h5sync
-        def thisismyname(foo):
-            """ I'm a docstring! """
-            pass
-        
-        self.assertEqual(thisismyname.__name__, "thisismyname")
-        self.assertEqual(thisismyname.__doc__, " I'm a docstring! ")
-
 
 
 
diff --git a/setup_cython.py b/setup_cython.py
index 52b93f9..a49d8dd 100644
--- a/setup_cython.py
+++ b/setup_cython.py
@@ -41,56 +41,21 @@ 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 
 
-class CmdOptions(object):
 
-    """ Manages package and compiler options. """
+# 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()
 
-    def __init__(self):
-
-        # Basic package options
-        self.NAME = 'h5py'
-        self.VERSION = '0.3.1'
-        self.MIN_NUMPY = '1.0.3'
-        self.MIN_CYTHON = '0.9.8.1'
-        self.KNOWN_API = (16,18)    # Legal API levels (1.8.X or 1.6.X)
-        self.SRC_PATH = 'h5py'      # Name of directory with .pyx files
-        self.CMD_CLASS = {}         # Custom command classes for setup()
-
-        # Compilation flags
-        self.CYTHON = False # Run Cython.  Default is OFF for distribution.
-        self.API = (16,)    # API levels to include.  Default is 1.6.X only.
-        self.DEBUG = 0      # Compile-time debug level.  Default is OFF.
-        self.HDF5 = None    # Custom HDF5 directory.
-
-        # Feature flags
-        self.THREADS = False    # Thread-aware (safety & non-blocking IO)
-
-    def get_pxi(self):
-        """ Return a string with compile-time defines. """
-
-        pxi_str = \
-"""# This file is automatically generated.  Do not edit.
-
-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
-
-"""
-        opts = dict(self.__dict__)
-        opts.update({   "API_MAX": max(self.API),
-                        "API_16": 16 in self.API,
-                        "API_18": 18 in self.API})
-        return pxi_str % opts
-
-        
-opts = CmdOptions()     # Global to hold compilation options
+# Compilation flags
+HDF5 = None            # Custom HDF5 directory.
+API = "16"
 
 def fatal(instring, code=1):
     print >> sys.stderr, "Fatal: "+instring
@@ -108,81 +73,30 @@ if not (sys.version_info[0:2] >= (2,5)):
 # Check for Numpy (required)
 try:
     import numpy
-    if numpy.version.version < opts.MIN_NUMPY:
-        fatal("Numpy version %s is out of date (>= %s needed)" % (numpy.version.version, opts.MIN_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)" % opts.MIN_NUMPY)
+    fatal("Numpy not installed (version >= %s required)" % MIN_NUMPY)
 
 # === Parse command line arguments ============================================
 
 for arg in sys.argv[:]:
 
-    if arg == '--cython':
-        opts.CYTHON = True
-        sys.argv.remove(arg)
-
-    elif arg.find('--api=') == 0:
-        try:
-            api = arg[6:]
-            api = tuple(int(x) for x in api.split(',') if len(x) > 0)
-            if len(api) == 0 or not all(x in opts.KNOWN_API for x in api):
-                raise Exception
-        except Exception:
-            fatal('Illegal option to --api= (legal values are %s)' % ','.join(str(x) for x in opts.KNOWN_API))
-        opts.API = api
-        opts.CYTHON = True
-        sys.argv.remove(arg)
-
-    elif arg.find('--debug=') == 0:
-        try:
-            opts.DEBUG = int(arg[8:])
-        except:
-            fatal('Debuglevel not understood (wants --debug=<n>)')
-        opts.CYTHON = True
-        sys.argv.remove(arg)
-
-    elif arg.find('--hdf5=') == 0:
+    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)
-        opts.HDF5 = path
-        opts.CYTHON = True
+        HDF5 = path
         sys.argv.remove(arg)
 
-    elif arg.find('--threads') == 0:
-        opts.THREADS = True
-        opts.CYTHON = True
-        sys.argv.remove(arg)
-
-# Check if the config.pxi file needs to be updated for the given
-# command-line options.
-pxi_path = op.join(opts.SRC_PATH, 'config.pxi')
-pxi = opts.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)
-    opts.CYTHON = True
-    if not "--force" in sys.argv: sys.argv.append("--force")  # Cython ignores .pxi change
-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)
-        opts.CYTHON = True
-        if not "--force" in sys.argv: sys.argv.append("--force")  # Cython ignores .pxi change
-    f.close()
+    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'):
@@ -197,36 +111,21 @@ 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 opts.API:
+if "18" in API:
     modules += ['h5o','h5l']
 
 # C source files required for Cython code (with extension)
 extra_src = ['utils_low.c']    
 
 
-if opts.CYTHON:
-    try:
-        from Cython.Compiler.Main import Version
-        from Cython.Distutils import build_ext
-    except ImportError:
-        fatal("Cython recompilation required, but Cython not installed.")
-
-    if Version.version < opts.MIN_CYTHON:
-        fatal("Old Cython version detected; at least %s required" % opts.MIN_CYTHON)
-
-    # This is what enables Cython.  Explicit calls to compile() don't work yet,
-    # which is really annoying and breaks a lot of the options behavior.
-    opts.CMD_CLASS.update({'build_ext': build_ext})
-
-
 # Platform-dependent arguments to setup() or Extension()
 if os.name == 'nt':
-    if opts.HDF5 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, 'include')]
-    library_dirs = [op.join(opts.HDF5, '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 = []
@@ -238,12 +137,12 @@ if os.name == 'nt':
 else:   # Assume Unix-like
 
     libraries = ['hdf5']
-    if opts.HDF5 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, 'include')]
-        library_dirs = [op.join(opts.HDF5, '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 = []
@@ -251,15 +150,12 @@ else:   # Assume Unix-like
     package_data = {'h5py': ['*.pyx'],
                    'h5py.tests': ['data/*.hdf5']}
 
-# Explicit list of source files for each module.
-mod_paths = [op.join(opts.SRC_PATH, x) for x in modules]
-extra_paths = [op.join(opts.SRC_PATH, x) for x in extra_src]
-if opts.CYTHON:
-    module_sources = [[x+'.pyx']+extra_paths for x in mod_paths]
-else:
-    module_sources = [[x+'.c']+extra_paths for x in mod_paths]
+# 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(opts.NAME+'.'+module,
+extensions = [ Extension(NAME+'.'+module,
                         sources, 
                         include_dirs = include_dirs, 
                         libraries = libraries,
@@ -272,22 +168,149 @@ extensions = [ Extension(opts.NAME+'.'+module,
 
 # === Custom extensions for distutils =========================================
 
-class test(Command):
+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 = [('sections=','s','Comma separated list of tests ("-" prefix to NOT run)')]
+    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()
+        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.")
@@ -316,8 +339,8 @@ class dev(Command):
                     shutil.rmtree(x)
                 except OSError:
                     pass
-            fnames = [ op.join(opts.SRC_PATH, x+'.dep') for x in modules ] + \
-                     [ op.join(opts.SRC_PATH, x+'.c') for x in modules ] + \
+            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:
@@ -350,9 +373,9 @@ class dev(Command):
 
 # New commands for setup (e.g. "python setup.py test")
 if os.name == 'nt':
-    opts.CMD_CLASS.update({'test': test})
+    CMD_CLASS.update({'build': cybuild, 'test': test})
 else:
-    opts.CMD_CLASS.update({'dev': dev, 'test': test})
+    CMD_CLASS.update({'build': cybuild, 'dev': dev, 'test': test})
 
 
 cls_txt = \
@@ -386,8 +409,8 @@ reading and writing data from Python.
 """
 
 setup(
-  name = opts.NAME,
-  version = opts.VERSION,
+  name = NAME,
+  version = VERSION,
   description = short_desc,
   long_description = long_desc,
   classifiers = [x for x in cls_txt.split("\n") if x],
@@ -399,8 +422,8 @@ setup(
   packages = ['h5py','h5py.tests'],
   package_data = package_data,
   ext_modules = extensions,
-  requires = ['numpy (>=%s)' % opts.MIN_NUMPY],
-  cmdclass = opts.CMD_CLASS
+  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