From 1f24437a256eb97adfff324f5bfc9cdc502de757 Mon Sep 17 00:00:00 2001 From: Matthew Brett Date: Thu, 21 Jul 2011 18:47:46 +0100 Subject: [PATCH 1/2] ENH: Added machinery for independent install --- .gitattributes | 1 + .gitignore | 4 + COPYING | 30 +++++ MANIFEST.in | 7 ++ Makefile | 40 ++++++ README.txt | 46 +++++++ formula/COMMIT_INFO.txt | 6 + formula/__init__.py | 7 ++ formula/info.py | 88 +++++++++---- formula/pkg_info.py | 83 +++++++++++++ formula/tests/__init__.py | 1 + nisext/__init__.py | 28 +++++ nisext/sexts.py | 141 +++++++++++++++++++++ nisext/testers.py | 252 ++++++++++++++++++++++++++++++++++++++ setup.py | 92 +++++++++++--- setup_egg.py | 13 ++ 16 files changed, 795 insertions(+), 44 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 COPYING create mode 100644 MANIFEST.in create mode 100644 Makefile create mode 100644 README.txt create mode 100644 formula/COMMIT_INFO.txt create mode 100644 formula/pkg_info.py create mode 100644 formula/tests/__init__.py create mode 100644 nisext/__init__.py create mode 100644 nisext/sexts.py create mode 100644 nisext/testers.py create mode 100644 setup_egg.py diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1e1cc11 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +formula/COMMIT_INFO.txt export-subst diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6cc140a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build/ +doc/_build/ +*.pyc +dist/ diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..932b352 --- /dev/null +++ b/COPYING @@ -0,0 +1,30 @@ +Copyright (c) 2009-2010, dipy developers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the dipy developers nor the names of any + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..0453979 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,7 @@ +include AUTHOR COPYING Makefile* MANIFEST.in setup* README.* +include Changelog TODO +recursive-include doc * +# put this stuff back into setup.py (package_data) once I'm enlightened +# enough to accomplish this herculean task +recursive-include formula/data * +include formula/COMMIT_INFO.txt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f9d4968 --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +PYTHON = python +COVERAGE_REPORT=coverage + +clean: + $(MAKE) -C doc clean + -rm -rf build + -rm *-stamp + +distclean: clean + -rm MANIFEST + -rm $(COVERAGE_REPORT) + @find . -name '*.py[co]' \ + -o -name '*.a' \ + -o -name '*,cover' \ + -o -name '.coverage' \ + -o -iname '*~' \ + -o -iname '*.kcache' \ + -o -iname '*.pstats' \ + -o -iname '*.prof' \ + -o -iname '#*#' | xargs -L10 rm -f + +# Print out info for possible install methods +check-version-info: + $(PYTHON) -c 'from nisext.testers import info_from_here; info_from_here("formula")' + +# Run tests from installed code +installed-tests: + $(PYTHON) -c 'from nisext.testers import tests_installed; tests_installed("formula")' + +# Run tests from installed code +sdist-tests: + $(PYTHON) -c 'from nisext.testers import sdist_tests; sdist_tests("formula")' + +bdist-egg-tests: + $(PYTHON) -c 'from nisext.testers import bdist_egg_tests; bdist_egg_tests("formula")' + +source-release: distclean + python -m compileall . + make distclean + python setup.py sdist --formats=gztar,zip diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..7e6d8ff --- /dev/null +++ b/README.txt @@ -0,0 +1,46 @@ +======= +Formula +======= + +This package contains an implementation of symbolic statistical models in +Python. + +The implementation is of a full algebra of factors and numerical variables in +statistical models. + +It has some similarities to model formulae in R, but differs in that the rules +for the creation of design matrices are more completely defined by the model +algebra. + +An earlier version of this package is in nipy_. + +Mailing Lists +============= + +Please see the developer's list here:: + + http://mail.scipy.org/mailman/listinfo/nipy-devel + +Code +==== + +You can find our sources and single-click downloads: + +* `Main repository`_ on Github. +* Documentation_ for all releases and current development tree. +* Download as a tar/zip file the `current trunk`_. +* Downloads of all `available releases`_. + +.. _nipy: http://nipy.org +.. _main repository: http://github.com/jonathan-taylor/formula +.. _documentation: http://github.com/jonathan-taylor/formula +.. _current trunk: http://github.com/jonathan-taylor/formula/archives/master +.. _available releases: http://github.com/jonathan-taylor/formula/downloads + +License +======= + +formula is licensed under the terms of the BSD license. Please the COPYING file +in the formula distribution. + +.. vim:syntax=rst diff --git a/formula/COMMIT_INFO.txt b/formula/COMMIT_INFO.txt new file mode 100644 index 0000000..dcaee0b --- /dev/null +++ b/formula/COMMIT_INFO.txt @@ -0,0 +1,6 @@ +# This is an ini file that may contain information about the code state +[commit hash] +# The line below may contain a valid hash if it has been substituted during 'git archive' +archive_subst_hash=$Format:%h$ +# This line may be modified by the install process +install_hash= diff --git a/formula/__init__.py b/formula/__init__.py index e69de29..ab820cb 100644 --- a/formula/__init__.py +++ b/formula/__init__.py @@ -0,0 +1,7 @@ +# Init for formula +import os + +from .info import __version__, long_description as __doc__ + +from .pkg_info import get_pkg_info as _get_pkg_info +get_info = lambda : _get_pkg_info(os.path.dirname(__file__)) diff --git a/formula/info.py b/formula/info.py index 2fbfc1e..d8c6d56 100644 --- a/formula/info.py +++ b/formula/info.py @@ -1,15 +1,16 @@ -""" This file contains defines parameters for nipy that we use to fill -settings in setup.py, the nipy top-level docstring, and for building the -docs. In setup.py in particular, we exec this file, so it cannot import nipy +""" This file contains defines parameters for formula that we use to fill +settings in setup.py, the formula top-level docstring, and for building the +docs. In setup.py in particular, we exec this file, so it cannot import formula """ -# nipy version information. An empty _version_extra corresponds to a +# formula version information. An empty _version_extra corresponds to a # full release. '.dev' as a _version_extra string means this is a development # version _version_major = 0 -_version_minor = 0 -_version_micro = 1 +_version_minor = 1 +_version_micro = 0 _version_extra = '.dev' +# _version_extra = '' # Format expected by setup.py and doc/source/conf.py: string of form "X.Y.Z" __version__ = "%s.%s.%s%s" % (_version_major, @@ -25,45 +26,80 @@ "Programming Language :: Python", "Topic :: Scientific/Engineering"] -description = 'A package to build design matrices for regression models' +description = 'Symbolic statistical formulae' # Note: this long_description is actually a copy/paste from the top-level # README.txt, so that it shows up nicely on PyPI. So please remember to edit # it only in one place and sync it correctly. -long_description = \ -""" +long_description = """ ======= Formula ======= -Formula is a python project for building -design matrices for regression models. +This package contains an implementation of symbolic statistical models in +Python. + +The implementation is of a full algebra of factors and numerical variables in +statistical models. + +It has some similarities to model formulae in R, but differs in that the rules +for the creation of design matrices are more completely defined by the model +algebra. + +An earlier version of this package is in nipy_. + +Mailing Lists +============= + +Please see the developer's list here:: + + http://mail.scipy.org/mailman/listinfo/nipy-devel + +Code +==== + +You can find our sources and single-click downloads: +* `Main repository`_ on Github. +* Documentation_ for all releases and current development tree. +* Download as a tar/zip file the `current trunk`_. +* Downloads of all `available releases`_. + +.. _nipy: http://nipy.org +.. _main repository: http://github.com/jonathan-taylor/formula +.. _documentation: http://github.com/jonathan-taylor/formula +.. _current trunk: http://github.com/jonathan-taylor/formula/archives/master +.. _available releases: http://github.com/jonathan-taylor/formula/downloads + +License +======= + +formula is licensed under the terms of the BSD license. Please the COPYING file +in the formula distribution. """ +# versions for dependencies +NUMPY_MIN_VERSION='1.2' +SYMPY_MIN_VERSION='0.7.0' + +# Main setup parameters NAME = 'formula' -MAINTAINER = "formula developers" -MAINTAINER_EMAIL = "" +MAINTAINER = "Jonathan Taylor" +MAINTAINER_EMAIL = "nipy-devel@neuroimaging.scipy.org" DESCRIPTION = description LONG_DESCRIPTION = long_description -URL = "" -DOWNLOAD_URL = "" +URL = "http://github.com/jonathan-taylor/formula" +DOWNLOAD_URL = "http://github.com/jonathan-taylor/formula/downloads" LICENSE = "BSD license" CLASSIFIERS = CLASSIFIERS -AUTHOR = "formula developers" -AUTHOR_EMAIL = "" +AUTHOR = "Jonathan Taylor" +AUTHOR_EMAIL = "nipy-devel@neuroimaging.scipy.org" PLATFORMS = "OS Independent" MAJOR = _version_major MINOR = _version_minor MICRO = _version_micro ISRELEASE = _version_extra == '' VERSION = __version__ -REQUIRES = ["numpy", "sympy"] -STATUS = 'alpha' - -# versions -NUMPY_MIN_VERSION='1.3' -SYMPY_MIN_VERSION = '0.6.7' - - - +PROVIDES = ["formula"] +REQUIRES = ["numpy (>=%s)" % NUMPY_MIN_VERSION, + "sympy (>=%s)" % SYMPY_MIN_VERSION] diff --git a/formula/pkg_info.py b/formula/pkg_info.py new file mode 100644 index 0000000..2ce4d23 --- /dev/null +++ b/formula/pkg_info.py @@ -0,0 +1,83 @@ +import os +import sys +import subprocess +from ConfigParser import ConfigParser + +COMMIT_INFO_FNAME = 'COMMIT_INFO.txt' + +def pkg_commit_hash(pkg_path): + ''' Get short form of commit hash given directory `pkg_path` + + There should be a file called 'COMMIT_INFO.txt' in `pkg_path`. This is a + file in INI file format, with at least one section: ``commit hash``, and two + variables ``archive_subst_hash`` and ``install_hash``. The first has a + substitution pattern in it which may have been filled by the execution of + ``git archive`` if this is an archive generated that way. The second is + filled in by the installation, if the installation is from a git archive. + + We get the commit hash from (in order of preference): + + * A substituted value in ``archive_subst_hash`` + * A written commit hash value in ``install_hash` + * git's output, if we are in a git repository + + If all these fail, we return a not-found placeholder tuple + + Parameters + ---------- + pkg_path : str + directory containing package + + Returns + ------- + hash_from : str + Where we got the hash from - description + hash_str : str + short form of hash + ''' + # Try and get commit from written commit text file + pth = os.path.join(pkg_path, COMMIT_INFO_FNAME) + if not os.path.isfile(pth): + raise IOError('Missing commit info file %s' % pth) + cfg_parser = ConfigParser() + cfg_parser.read(pth) + archive_subst = cfg_parser.get('commit hash', 'archive_subst_hash') + if not archive_subst.startswith('$Format'): # it has been substituted + return 'archive substitution', archive_subst + install_subst = cfg_parser.get('commit hash', 'install_hash') + if install_subst != '': + return 'installation', install_subst + # maybe we are in a repository + proc = subprocess.Popen('git rev-parse --short HEAD', + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=pkg_path, shell=True) + repo_commit, _ = proc.communicate() + if repo_commit: + return 'repository', repo_commit.strip() + return '(none found)', '' + + +def get_pkg_info(pkg_path): + ''' Return dict describing the context of this package + + Parameters + ---------- + pkg_path : str + path containing __init__.py for package + + Returns + ------- + context : dict + with named parameters of interest + ''' + src, hsh = pkg_commit_hash(pkg_path) + import numpy + return dict( + pkg_path=pkg_path, + commit_source=src, + commit_hash=hsh, + sys_version=sys.version, + sys_executable=sys.executable, + sys_platform=sys.platform, + np_version=numpy.__version__) diff --git a/formula/tests/__init__.py b/formula/tests/__init__.py new file mode 100644 index 0000000..7a8947f --- /dev/null +++ b/formula/tests/__init__.py @@ -0,0 +1 @@ +# Make tests a package diff --git a/nisext/__init__.py b/nisext/__init__.py new file mode 100644 index 0000000..2163c3b --- /dev/null +++ b/nisext/__init__.py @@ -0,0 +1,28 @@ +# init for sext package +""" Setuptools extensions that can be shared across projects + +Typical use for these routines is as a git subtree merge + +For example:: + + # Add a remote pointing to repository + git remote add nisext git://github.com/nipy/nisext.git + git fetch nisext + # Label nisext history as merged + git merge -s ours --no-commit nisext/master + # Read nisext contents as nisext subdirectory + git read-tree --prefix=nisext/ -u nisext/master + git commit -m "Merge nisext project as subtree" + +Then you would typically add a makefile target like:: + + # Update nisext subtree from remote + update-nisext: + git fetch nisext + git merge --squash -s subtree --no-commit nisext/master + +and commit when you have changes you want. This allows you to keep the nisext +tree updated from the upstream repository, but the tree will be there and ready +for someone without this machinery or remote. +""" + diff --git a/nisext/sexts.py b/nisext/sexts.py new file mode 100644 index 0000000..838bb42 --- /dev/null +++ b/nisext/sexts.py @@ -0,0 +1,141 @@ +''' Distutils / setuptools helpers ''' + +from os.path import join as pjoin +try: + from ConfigParser import ConfigParser +except ImportError: + from configparser import ConfigParser + + +from distutils.version import LooseVersion +from distutils.command.build_py import build_py + +from distutils import log + +def get_comrec_build(pkg_dir, build_cmd=build_py): + """ Return extended build command class for recording commit + + The extended command tries to run git to find the current commit, getting + the empty string if it fails. It then writes the commit hash into a file + in the `pkg_dir` path, named ``COMMIT_INFO.txt``. + + In due course this information can be used by the package after it is + installed, to tell you what commit it was installed from if known. + + To make use of this system, you need a package with a COMMIT_INFO.txt file - + e.g. ``myproject/COMMIT_INFO.txt`` - that might well look like this:: + + # This is an ini file that may contain information about the code state + [commit hash] + # The line below may contain a valid hash if it has been substituted during 'git archive' + archive_subst_hash=$Format:%h$ + # This line may be modified by the install process + install_hash= + + The COMMIT_INFO file above is also designed to be used with git substitution + - so you probably also want a ``.gitattributes`` file in the root directory + of your working tree that contains something like this:: + + myproject/COMMIT_INFO.txt export-subst + + That will cause the ``COMMIT_INFO.txt`` file to get filled in by ``git + archive`` - useful in case someone makes such an archive - for example with + via the github 'download source' button. + + Although all the above will work as is, you might consider having something + like a ``get_info()`` function in your package to display the commit + information at the terminal. See the ``pkg_info.py`` module in the nipy + package for an example. + """ + class MyBuildPy(build_cmd): + ''' Subclass to write commit data into installation tree ''' + def run(self): + build_cmd.run(self) + import subprocess + proc = subprocess.Popen('git rev-parse --short HEAD', + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True) + repo_commit, _ = proc.communicate() + # Fix for python 3 + repo_commit = str(repo_commit) + # We write the installation commit even if it's empty + cfg_parser = ConfigParser() + cfg_parser.read(pjoin(pkg_dir, 'COMMIT_INFO.txt')) + cfg_parser.set('commit hash', 'install_hash', repo_commit) + out_pth = pjoin(self.build_lib, pkg_dir, 'COMMIT_INFO.txt') + cfg_parser.write(open(out_pth, 'wt')) + return MyBuildPy + + +# Dependency checks +def package_check(pkg_name, version=None, + optional=False, + checker=LooseVersion, + version_getter=None, + messages=None + ): + ''' Check if package `pkg_name` is present, and correct version + + Parameters + ---------- + pkg_name : str + name of package as imported into python + version : {None, str}, optional + minimum version of the package that we require. If None, we don't + check the version. Default is None + optional : {False, True}, optional + If False, raise error for absent package or wrong version; + otherwise warn + checker : callable, optional + callable with which to return comparable thing from version + string. Default is ``distutils.version.LooseVersion`` + version_getter : {None, callable}: + Callable that takes `pkg_name` as argument, and returns the + package version string - as in:: + + ``version = version_getter(pkg_name)`` + + If None, equivalent to:: + + mod = __import__(pkg_name); version = mod.__version__`` + messages : None or dict, optional + dictionary giving output messages + ''' + if version_getter is None: + def version_getter(pkg_name): + mod = __import__(pkg_name) + return mod.__version__ + if messages is None: + messages = {} + msgs = { + 'missing': 'Cannot import package "%s" - is it installed?', + 'missing opt': 'Missing optional package "%s"', + 'opt suffix' : '; you may get run-time errors', + 'version too old': 'You have version %s of package "%s"' + ' but we need version >= %s', } + msgs.update(messages) + try: + __import__(pkg_name) + except ImportError: + if not optional: + raise RuntimeError(msgs['missing'] % pkg_name) + log.warn(msgs['missing opt'] % pkg_name + + msgs['opt suffix']) + return + if not version: + return + try: + have_version = version_getter(pkg_name) + except AttributeError: + raise RuntimeError('Cannot find version for %s' % pkg_name) + if checker(have_version) < checker(version): + if optional: + log.warn(msgs['version too old'] % (have_version, + pkg_name, + version) + + msgs['opt suffix']) + else: + raise RuntimeError(msgs['version too old'] % (have_version, + pkg_name, + version)) diff --git a/nisext/testers.py b/nisext/testers.py new file mode 100644 index 0000000..e51278f --- /dev/null +++ b/nisext/testers.py @@ -0,0 +1,252 @@ +''' Test package information in various install settings + +The routines here install the package in various settings and print out the +corresponding version info from the installation. + +The typical use for this module is as a makefile target, as in:: + + # Print out info for possible install methods + check-version-info: + python -c 'from nisext.testers import info_from_here; info_from_here("mypackage")' + +''' + +import os +from os.path import join as pjoin +from glob import glob +import shutil +import tempfile +import zipfile +from subprocess import call +from functools import partial + +my_call = partial(call, shell=True) + +PY_LIB_SDIR = 'pylib' + +def run_mod_cmd(mod_name, pkg_path, cmd): + ''' Run command in own process in anonymous path + + Parameters + ---------- + mod_name : str + Name of module to import - e.g. 'nibabel' + pkg_path : str + directory containing `mod_name` package. Typically that will be the + directory containing the e.g. 'nibabel' directory. + ''' + cwd = os.getcwd() + tmpdir = tempfile.mkdtemp() + try: + os.chdir(tmpdir) + my_call('python -c "import sys; sys.path.insert(1,\'%s\'); ' + 'import %s;' + 'print %s.__file__;' + '%s"' % (pkg_path, + mod_name, + mod_name, + cmd)) + finally: + os.chdir(cwd) + shutil.rmtree(tmpdir) + + +def zip_extract_all(fname, path=None): + ''' Extract all members from zipfile + + Deals with situation where the directory is stored in the zipfile as a name, + as well as files that have to go into this directory. + ''' + zf = zipfile.ZipFile(fname) + members = zf.namelist() + # Remove members that are just bare directories + members = [m for m in members if not m.endswith('/')] + for zipinfo in members: + zf.extract(zipinfo, path, None) + + +def install_from_to(from_dir, to_dir, py_lib_sdir): + """ Install package in `from_dir` to standard location in `to_dir` + + Return path to directory containing package directory. The package directory + is the directory containing __init__.py + """ + site_pkgs_path = os.path.join(to_dir, py_lib_sdir) + py_lib_locs = ' --install-purelib=%s --install-platlib=%s' % ( + site_pkgs_path, site_pkgs_path) + pwd = os.path.abspath(os.getcwd()) + try: + os.chdir(from_dir) + my_call('python setup.py --quiet install --prefix=%s %s' % (to_dir, + py_lib_locs)) + finally: + os.chdir(pwd) + return site_pkgs_path + + +def check_installed_files(repo_mod_path, install_mod_path): + """ Check files in `repo_mod_path` are installed at `install_mod_path` + + At the moment, all this does is check that all the ``*.py`` files in + `repo_mod_path` are installed at `install_mod_path`. + + Parameters + ---------- + repo_mod_path : str + repository path containing package files, e.g. /nibabel> + install_mod_path : str + path at which package has been installed. This is the path where the + root package ``__init__.py`` lives. + + Return + ------ + uninstalled : list + list of files that should have been installed, but have not been + installed + """ + repo_mod_path = os.path.abspath(repo_mod_path) + uninstalled = [] + # Walk directory tree to get py files + for dirpath, dirnames, filenames in os.walk(repo_mod_path): + out_dirpath = dirpath.replace(repo_mod_path, install_mod_path) + for fname in filenames: + if not fname.lower().endswith('.py'): + continue + equiv_fname = os.path.join(out_dirpath, fname) + if not os.path.isfile(equiv_fname): + uninstalled.append(pjoin(dirpath, fname)) + return uninstalled + + +def contexts_print_info(mod_name, repo_path, install_path): + ''' Print result of get_info from different installation routes + + Runs installation from: + + * git archive zip file + * with setup.py install from repository directory + * just running code from repository directory + + and prints out result of get_info in each case. There will be many files + written into `install_path` that you may want to clean up somehow. + + Parameters + ---------- + mod_name : str + package name that will be installed, and tested + repo_path : str + path to location of git repository + install_path : str + path into which to install temporary installations + ''' + site_pkgs_path = os.path.join(install_path, PY_LIB_SDIR) + # first test archive + pwd = os.path.abspath(os.getcwd()) + out_fname = pjoin(install_path, 'test.zip') + try: + os.chdir(repo_path) + my_call('git archive --format zip -o %s HEAD' % out_fname) + finally: + os.chdir(pwd) + install_from = pjoin(install_path, mod_name) + zip_extract_all(out_fname, install_from) + site_pkgs_path = install_from_to(install_from, + install_path, + PY_LIB_SDIR) + cmd_str = 'print %s.get_info()' % mod_name + run_mod_cmd(mod_name, site_pkgs_path, cmd_str) + # now test install into a directory from the repository + site_pkgs_path = install_from_to(repo_path, + install_path, + PY_LIB_SDIR) + run_mod_cmd(mod_name, site_pkgs_path, cmd_str) + # Take the opportunity to audit the py files + repo_mod_path = os.path.join(repo_path, mod_name) + install_mod_path = os.path.join(site_pkgs_path, mod_name) + print 'Files not taken across by the installation:' + print check_installed_files(repo_mod_path, install_mod_path) + # test from development tree + run_mod_cmd(mod_name, repo_path, cmd_str) + return + + +def info_from_here(mod_name): + ''' Run info context checks starting in working directory + + Runs checks from current working directory, installing temporary + installations into a new temporary directory + + Parameters + ---------- + mod_name : str + package name that will be installed, and tested + ''' + repo_path = os.path.abspath(os.getcwd()) + install_path = tempfile.mkdtemp() + try: + contexts_print_info(mod_name, repo_path, install_path) + finally: + shutil.rmtree(install_path) + + +def tests_installed(mod_name, source_path=None): + """ Install from `source_path` into temporary directory; run tests + + Parameters + ---------- + mod_name : str + name of module - e.g. 'nibabel' + source_path : None or str + Path from which to install. If None, defaults to working directory + """ + if source_path is None: + source_path = os.path.abspath(os.getcwd()) + install_path = tempfile.mkdtemp() + try: + site_pkgs_path = install_from_to(source_path, + install_path, + PY_LIB_SDIR) + run_mod_cmd(mod_name, site_pkgs_path, mod_name + '.test()') + finally: + shutil.rmtree(install_path) + +# Tell nose this is not a test +tests_installed.__test__ = False + + +def tests_from_zip(mod_name, zip_fname): + """ Runs test from sdist zip source archive """ + install_path = tempfile.mkdtemp() + try: + zip_extract_all(zip_fname, install_path) + pkg_dirs = glob(pjoin(install_path, mod_name + "*")) + if len(pkg_dirs) != 1: + raise OSError('There must be one and only one package dir') + pkg_contents = pjoin(install_path, pkg_dirs[0]) + tests_installed(mod_name, pkg_contents) + finally: + shutil.rmtree(install_path) + +tests_from_zip.__test__ = False + + +def sdist_tests(mod_name, repo_path=None): + """ Make sdist zip, install from it, and run tests """ + pwd = os.path.abspath(os.getcwd()) + if repo_path is None: + repo_path = pwd + install_path = tempfile.mkdtemp() + try: + os.chdir(repo_path) + my_call('python setup.py sdist --formats=zip --dist-dir=' + + install_path) + zip_fnames = glob(pjoin(install_path, mod_name + "*.zip")) + if len(zip_fnames) != 1: + raise OSError('There must be one and only one zip file, ' + 'but I found "%s"' % ': '.join(zip_fnames)) + tests_from_zip(mod_name, zip_fnames[0]) + finally: + os.chdir(pwd) + shutil.rmtree(install_path) + +sdist_tests.__test__ = False diff --git a/setup.py b/setup.py index bb0a1c8..7b25aae 100755 --- a/setup.py +++ b/setup.py @@ -1,28 +1,84 @@ """ -A package to solve separable convex optimization problems, based on +Symbolic formulae for statistical models """ -import os, sys -import string +#!/usr/bin/env python +# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +# +# See COPYING file distributed along with the NiBabel package for the +# copyright and license terms. +# +### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ## +"""Build helper.""" -from Cython.Compiler import Main +import os +from os.path import join as pjoin +import sys -def cython_extension(srcfile): - options = Main.CompilationOptions(include_path=[os.path.join(os.path.abspath(os.path.dirname(__file__)), 'include')]) - Main.compile(srcfile, options=options) +# BEFORE importing distutils, remove MANIFEST. distutils doesn't properly +# update it when the contents of directories change. +if os.path.exists('MANIFEST'): os.remove('MANIFEST') -def configuration(parent_package='',top_path=None): - from numpy.distutils.misc_util import Configuration - config = Configuration(None,parent_package,top_path) +from distutils.core import setup - config.add_subpackage('formula') - config.add_data_dir("formula/data") - return config +# For some commands, use setuptools. +if len(set(('develop', 'bdist_egg', 'bdist_rpm', 'bdist', 'bdist_dumb', + 'bdist_wininst', 'install_egg_info', 'egg_info', 'easy_install', + )).intersection(sys.argv)) > 0: + # setup_egg imports setuptools setup, thus monkeypatching distutils. + import setup_egg -if __name__ == '__main__': +from nisext.sexts import get_comrec_build, package_check +cmdclass = {'build_py': get_comrec_build('formula')} - from numpy.distutils.core import setup +# Get version and release info, which is all stored in formula/info.py +ver_file = os.path.join('formula', 'info.py') +execfile(ver_file) - c = configuration(top_path='', - ).todict() - setup(**c) +# Do dependency checking +package_check('numpy', NUMPY_MIN_VERSION) +package_check('sympy', SYMPY_MIN_VERSION) +extra_setuptools_args = {} +if 'setuptools' in sys.modules: + extra_setuptools_args['extras_require'] = dict( + doc='Sphinx>=0.3', + test='nose>=0.10.1', + ) + +def main(**extra_args): + setup(name=NAME, + maintainer=MAINTAINER, + maintainer_email=MAINTAINER_EMAIL, + description=DESCRIPTION, + long_description=LONG_DESCRIPTION, + url=URL, + download_url=DOWNLOAD_URL, + license=LICENSE, + classifiers=CLASSIFIERS, + author=AUTHOR, + author_email=AUTHOR_EMAIL, + platforms=PLATFORMS, + version=VERSION, + requires=REQUIRES, + provides=PROVIDES, + packages = ['formula', + 'formula.tests'], + # The package_data spec has no effect for me (on python 2.6) -- even + # changing to data_files doesn't get this stuff included in the source + # distribution -- not sure if it has something to do with the magic + # above, but distutils is surely the worst piece of code in all of + # python -- duplicating things into MANIFEST.in but this is admittedly + # only a workaround to get things started -- not a solution + package_data = {'formula': + [pjoin('data', '*'), + ]}, + scripts = [], + cmdclass = cmdclass, + **extra_args + ) + + +if __name__ == "__main__": + main(**extra_setuptools_args) diff --git a/setup_egg.py b/setup_egg.py new file mode 100644 index 0000000..48aafbc --- /dev/null +++ b/setup_egg.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +"""Wrapper to run setup.py using setuptools.""" + +import setuptools + +################################################################################ +# Call the setup.py script, injecting the setuptools-specific arguments. + +if __name__ == '__main__': + exec(open('setup.py', 'rt').read(), dict(__name__='__main__')) + From f833a3893f0372410cce30991f3325d1c6dfcb34 Mon Sep 17 00:00:00 2001 From: Matthew Brett Date: Fri, 22 Jul 2011 00:21:29 +0100 Subject: [PATCH 2/2] BF: remove reference to dipy --- COPYING | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/COPYING b/COPYING index 932b352..a1fff25 100644 --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -Copyright (c) 2009-2010, dipy developers +Copyright (c) 2005-2011, nipy developers All rights reserved. Redistribution and use in source and binary forms, with or without @@ -13,7 +13,7 @@ met: disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of the dipy developers nor the names of any + * Neither the name of the nipy developers nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.