From b6dcf3c336281464b4b344b77ab1fae1164a15ab Mon Sep 17 00:00:00 2001 From: blink1073 Date: Fri, 18 Jul 2014 16:12:51 -0500 Subject: [PATCH 01/11] Update Travis build to use Anaconda. Update Travis build to use Anaconda Travis updates and fixes More travis fixes Another travis attempt Revert changes Use PIL and Pillow Refactor travis into 4 different builds Fix activation error Remove explicit mpl in build_versions.py Make matplotlib an explicit requirement Rearrange travis Make pillow a hard requirement Try again to make Pillow optional Fix bash syntax error Fix bash syntax error Bump required cython version More rearrangments Remove mpl from build_versions, rearrange travis Fix version check Make matplotlib explicit again Conda install into test env Check for proper install Allow tests to skip if networkx is not available Allow tests to skip if networkx is not available Try swapping pillow for matplotlib Allow tests to pass when matplotlib is not present Remove matplotlib from build_versions Print PIL version Get pillow from PIP Allow tests to skip if matplotlib is not present. Allow tests to skip if networkx is not present. travis fix Remove unused mpl import that caused test error Use nose-cov and do not run doctests without optional libs Bump required numpy version and fix nose calls Make overlay test repeatable bump numpy version again Move low-end numpy to python 2.7 Play with minimum versions Add version requirements and use functions Add version requirements and use functions Allow require to skip a test More implementation of require decorator Update require decorator and clean up tests Only use requires decorator when needed Fix python3 error in version_requirements Fix build errors Fix handling of require with tests More fixes for require handler Use latest miniconda Fix more build errors Fix another dict comprehension and travis file. Fix missing imports Fix dictionary again Fix import warning Fix last failing test on 2.6 Skip doc examples on python2.6 Do not run doctests on python2.6 Fix typo in travis.yml Make numpy-1.6 compatibility changes Use numpy-1.6 in travis python2.6 Add tests for version requirements Fix line noise in PR Add additional io plugins Fix simpleitk test. Fix python 3 error in freeimage_plugin. Install imread in Travis. Put matplotlib settings in XDG recommended directory Fix formatting in travis yml Fix formatting in travis yml Make sure to close PIL file atexit Fix name of apt package xcftools Fix pil fp closing Fix matplotlibrc creation Only download SimpleITK on py2x, run coverage on py27 Fix travis yml syntax error Run coveralls on py2.7 Install SimpleITK on py3.3 and run coverage on py3.3 Make simpleitk install quiet Use standard nose and clean up incantation Fix travis yml syntax error Put in miniconda workout for libc error. Fix imread plugin. Fix travis syntax Remove unused import Remove miniconda libpng in favor of system png Fix imread install and move libm removal to after optional pkg install. Fix png header copy in travis yml Another attempt to use png headers Debug freeimage Add jpeg library for freeimage and debug imread. More debug for imread and freeimage More freeimage and imread debugging More debugging Use correct paths for test env Make sure imread is tied to libpng15 Add a TODO note for simpleitk test causing error. Fix typo in yml Cleanup and add more comments to travis yml Update comment Try and add 3.2 support. Docstring formatting Add more travis comments. Try numpy 1.6 on python 2.7 Fix travis syntax error Rename CONDA to ENV for clarity Alias python on python 3.2 Use python 3.2 as the system python Clean up libfreeimage install Fix order on py3.2 pre_install Move old numpy back to py26 Use the appropriate python calls. Debug 3.2 build. Update comment Fix syntax error Another fix for syntax error. Install scipy after downloading import tools More debugging for py32 Do not install conda on py3.2 (duh) Fix typo in travis yml Fix py32 qt install, separate pyfits and imread to find error Fix syntax error and front-load option lib check for debug pyfits is not supported in py3.2, try imread now imread is also not supported on py3.2 install imread before pyfits to show relationship with libs Make pip builds quiet Minor formatting to retrigger build Allow simpleitk to fail to download without breaking the build Use travis_retry for SimpleITK See what breaks when we keep libm in Now remove libm again --- .travis.yml | 145 ++++++++++-------- skimage/exposure/_adapthist.py | 4 +- skimage/feature/tests/test_util.py | 6 +- skimage/graph/graph_cut.py | 7 +- skimage/graph/rag.py | 9 +- skimage/graph/tests/test_rag.py | 3 + skimage/io/_plugins/freeimage_plugin.py | 2 +- skimage/io/_plugins/imread_plugin.py | 2 +- skimage/io/_plugins/pil_plugin.py | 6 +- skimage/io/_plugins/qt_plugin.py | 2 +- skimage/io/tests/test_imread.py | 5 +- skimage/io/tests/test_simpleitk.py | 10 +- skimage/morphology/greyreconstruct.py | 2 +- skimage/morphology/misc.py | 5 +- skimage/restoration/tests/test_unwrap.py | 7 + skimage/restoration/unwrap.py | 2 + .../util/tests/test_version_requirements.py | 46 ++++++ skimage/util/version_requirements.py | 125 +++++++++++++++ skimage/viewer/__init__.py | 10 ++ skimage/viewer/canvastools/base.py | 4 +- skimage/viewer/canvastools/linetool.py | 3 +- skimage/viewer/canvastools/painttool.py | 12 +- skimage/viewer/canvastools/recttool.py | 1 - skimage/viewer/plugins/color_histogram.py | 6 +- skimage/viewer/plugins/overlayplugin.py | 11 +- skimage/viewer/tests/test_plugins.py | 20 +-- skimage/viewer/tests/test_tools.py | 18 +-- skimage/viewer/tests/test_utils.py | 21 ++- skimage/viewer/tests/test_viewer.py | 20 +-- skimage/viewer/tests/test_widgets.py | 15 +- skimage/viewer/utils/core.py | 11 +- skimage/viewer/viewers/core.py | 1 - tools/build_versions.py | 11 +- 33 files changed, 380 insertions(+), 172 deletions(-) create mode 100644 skimage/util/tests/test_version_requirements.py create mode 100644 skimage/util/version_requirements.py diff --git a/.travis.yml b/.travis.yml index 1f292dfa..1ef5b85d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,28 +3,17 @@ # After changing this file, check it on: # http://lint.travis-ci.org/ - language: python python: - - 2.6 + - 3.2 -matrix: - include: - - python: 2.7 - env: - - PYTHON=python - - PYTHONWARNINGS=all - - PYTHONX=python - - PYVER=2.x - - python: 3.2 - env: - - PYTHON=python3 - - PYTHONWARNINGS=all - - PYTHONX=python3 - - PYVER=3.x - exclude: - - python: 2.6 +env: + - ENV="python=2.6 numpy=1.6 cython=0.19.2" + - ENV="python=2.7 numpy cython" + - ENV="python=3.2" + - ENV="python=3.3 numpy cython" + - ENV="python=3.4 numpy cython" virtualenv: system_site_packages: true @@ -32,64 +21,92 @@ virtualenv: before_install: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start - - sudo apt-get update - - sudo apt-get install $PYTHON-numpy - - wget https://raw.githubusercontent.com/numpy/numpy/master/numpy/_import_tools.py -O /home/travis/virtualenv/python3.2_with_system_site_packages/lib/python3.2/site-packages/numpy/_import_tools.py - - - sudo apt-get install $PYTHON-scipy - - sudo apt-get install libfreeimage3 - - - if [[ $PYVER == '2.x' ]]; then - - sudo apt-get install $PYTHON-qt4; - - sudo apt-get install $PYTHON-matplotlib; - - fi - - if [[ $PYVER == '3.x' ]]; then - - sudo apt-get install $PYTHON-pyqt4; - - pip install --use-mirrors matplotlib; - - fi - - - pip install pillow - - pip install cython - - pip install flake8 - - pip install six - - pip install networkx - - - pip install nose-cov - - pip install coveralls - + - if [[ $ENV == python=3.2 ]]; then + sudo apt-get install python3-numpy; + wget https://raw.githubusercontent.com/numpy/numpy/master/numpy/_import_tools.py -O /home/travis/virtualenv/python3.2_with_system_site_packages/lib/python3.2/site-packages/numpy/_import_tools.py; + sudo apt-get install python3-scipy; + pip install cython flake8 six; + else + wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh; + bash miniconda.sh -b -p $HOME/miniconda; + export PATH="$HOME/miniconda/bin:$PATH"; + hash -r; + conda config --set always_yes yes; + conda update conda; + conda info -a; + conda create -n test $ENV six scipy pip flake8 nose; + source activate test; + fi + - pip install coveralls pillow - python check_bento_build.py install: - tools/header.py "Dependency versions" - tools/build_versions.py - + - export PYTHONWARNINGS=all - python setup.py build_ext --inplace script: - # Matplotlib settings - - mkdir -p $HOME/.matplotlib - - touch $HOME/.matplotlib/matplotlibrc - - "echo 'backend : Agg' > $HOME/.matplotlib/matplotlibrc" - - "echo 'backend.qt4 : PyQt4' >> $HOME/.matplotlib/matplotlibrc" - - # Run all tests - - if [[ $PYVER == '3.x' ]]; then - - nosetests --exe -v --with-doctest --with-cov --cov skimage --cov-config=.coveragerc skimage - - fi - - if [[ $PYVER == '2.x' ]]; then - - nosetests --exe -v --with-doctest skimage - - fi - # Run all doc examples - - export PYTHONPATH=$(pwd):$PYTHONPATH - - for f in doc/examples/*.py; do $PYTHONX "$f"; if [ $? -ne 0 ]; then exit 1; fi done - - for f in doc/examples/applications/*.py; do $PYTHONX "$f"; if [ $? -ne 0 ]; then exit 1; fi done + # Run all tests with minimum dependencies + - nosetests --exe -v skimage # Run pep8 and flake tests - flake8 --exit-zero --exclude=test_*,six.py skimage doc/examples viewer_examples + # Install optional dependencies to get full test coverage + # Notes: + # - pyfits and imread do NOT support py3.2 + # - Use the png headers included in anaconda (from matplotlib install) + # TODO: Remove the libm removal when anaconda fixes their libraries + # The solution was suggested here + # https://groups.google.com/a/continuum.io/forum/#!topic/anaconda/-DLG2ZdTkw0 + - if [[ $ENV == python=3.2 ]]; then + sudo apt-get install -qq python3-pyqt4; + pip install --use-mirrors -q matplotlib; + pip install -q networkx; + else + conda install -q matplotlib pyqt networkx; + sudo cp ~/miniconda/envs/test/include/png* /usr/include; + rm ~/miniconda/envs/test/lib/libm-2.5.so; + rm ~/miniconda/envs/test/lib/libm.*; + sudo apt-get install -qq libtiff4-dev libwebp-dev xcftools; + pip install -q imread; + pip install -q pyfits; + fi + - sudo apt-get install -qq libfreeimage3 + # TODO: update when SimpleITK become available on py34 or hopefully pip + - if [[ $ENV != python=3.4* ]]; then + travis_retry easy_install -q SimpleITK; + fi + + # Matplotlib settings + - export MPL_DIR=$HOME/.config/matplotlib + - mkdir -p $MPL_DIR + - touch $MPL_DIR/matplotlibrc + - "echo 'backend : Agg' > $MPL_DIR/matplotlibrc" + - "echo 'backend.qt4 : PyQt4' >> $MPL_DIR/matplotlibrc" + + # Run all doc examples + - export PYTHONPATH=$(pwd):$PYTHONPATH + - if [[ $ENV != python=2.6* ]]; then + for f in doc/examples/*.py; do python "$f"; if [ $? -ne 0 ]; then exit 1; fi done + fi + - for f in doc/examples/applications/*.py; do python "$f"; if [ $? -ne 0 ]; then exit 1; fi done + + # run tests again with optional dependencies to get more coverage + # measure coverage on py3.3 + # doctests fail on python 2.6 for restoration/unwrap + - if [[ $ENV == python=3.3* ]]; then + nosetests --exe -v --with-doctest --with-cov --cover-package skimage; + elif [[ $ENV == python=2.6* ]]; then + nosetests --exe -v skimage; + else + nosetests --exe -v --with-doctest skimage; + fi + after_success: - - if [[ $PYVER == '3.x' ]]; then - - coveralls - - fi + - if [[ $ENV == python=3.3* ]]; then + coveralls; + fi diff --git a/skimage/exposure/_adapthist.py b/skimage/exposure/_adapthist.py index 5da50029..9cfc716e 100644 --- a/skimage/exposure/_adapthist.py +++ b/skimage/exposure/_adapthist.py @@ -17,7 +17,7 @@ import numpy as np import skimage from skimage.color.adapt_rgb import adapt_rgb, hsv_value from skimage.exposure import rescale_intensity -from skimage.util import view_as_blocks +from skimage.util import view_as_blocks, pad MAX_REG_X = 16 # max. # contextual regions in x-direction */ @@ -125,7 +125,7 @@ def _clahe(image, ntiles_x, ntiles_y, clip_limit, nbins=128): if h_inner != image.shape[1] or w_inner != image.shape[0]: h_pad = height * ntiles_y - image.shape[0] w_pad = width * ntiles_x - image.shape[1] - image = np.pad(image, ((0, h_pad), (0, w_pad)), 'reflect') + image = pad(image, ((0, h_pad), (0, w_pad)), mode='reflect') h_inner, w_inner = image.shape bin_size = 1 + NR_OF_GREY / nbins diff --git a/skimage/feature/tests/test_util.py b/skimage/feature/tests/test_util.py index 2b601b94..f15bcb2a 100644 --- a/skimage/feature/tests/test_util.py +++ b/skimage/feature/tests/test_util.py @@ -1,5 +1,8 @@ import numpy as np -import matplotlib.pyplot as plt +try: + import matplotlib.pyplot as plt +except ImportError: + plt = None from numpy.testing import assert_equal, assert_raises from skimage.feature.util import (FeatureDetector, DescriptorExtractor, @@ -39,6 +42,7 @@ def test_mask_border_keypoints(): [0, 0, 0, 0, 1]) +@np.testing.decorators.skipif(plt is None) def test_plot_matches(): fig, ax = plt.subplots(nrows=1, ncols=1) diff --git a/skimage/graph/graph_cut.py b/skimage/graph/graph_cut.py index 91d64aec..443b1561 100644 --- a/skimage/graph/graph_cut.py +++ b/skimage/graph/graph_cut.py @@ -1,4 +1,9 @@ -import networkx as nx +try: + import networkx as nx +except ImportError: + import warnings + warnings.warn('"cut_threshold" requires networkx') + nx = None import numpy as np diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index 84721736..fc4a1aaa 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -1,4 +1,11 @@ -import networkx as nx +try: + import networkx as nx +except ImportError: + class nx: + class Graph: + pass + import warnings + warnings.warn('Region Adjacency Graph (RAG) features require networkx') import numpy as np from scipy.ndimage import filters from scipy import ndimage as nd diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index af7481eb..b6f95cb2 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -1,5 +1,6 @@ import numpy as np from skimage import graph +from skimage.util.version_requirements import require def max_edge(g, src, dst, n): @@ -9,6 +10,7 @@ def max_edge(g, src, dst, n): return max(w1, w2) +@require('networkx') def test_rag_merge(): g = graph.rag.RAG() @@ -43,6 +45,7 @@ def test_rag_merge(): assert g.edges() == [] +@require('networkx') def test_threshold_cut(): img = np.zeros((100, 100, 3), dtype='uint8') diff --git a/skimage/io/_plugins/freeimage_plugin.py b/skimage/io/_plugins/freeimage_plugin.py index 07e8d313..5ebe8214 100644 --- a/skimage/io/_plugins/freeimage_plugin.py +++ b/skimage/io/_plugins/freeimage_plugin.py @@ -85,7 +85,7 @@ def load_freeimage(): if errors: # No freeimage library loaded, and load-errors reported for some # candidate libs - err_txt = ['%s:\n%s' % (l, str(e.message)) for l, e in errors] + err_txt = ['%s:\n%s' % (l, str(e)) for l, e in errors] raise RuntimeError('One or more FreeImage libraries were found, but ' 'could not be loaded due to the following errors:\n' '\n\n'.join(err_txt)) diff --git a/skimage/io/_plugins/imread_plugin.py b/skimage/io/_plugins/imread_plugin.py index 46382cdf..cb715e57 100644 --- a/skimage/io/_plugins/imread_plugin.py +++ b/skimage/io/_plugins/imread_plugin.py @@ -1,6 +1,6 @@ __all__ = ['imread', 'imsave'] -from skimage.utils.dtype import convert +from skimage.util.dtype import convert try: import imread as _imread diff --git a/skimage/io/_plugins/pil_plugin.py b/skimage/io/_plugins/pil_plugin.py index 88e7f04b..e6f9c491 100644 --- a/skimage/io/_plugins/pil_plugin.py +++ b/skimage/io/_plugins/pil_plugin.py @@ -21,6 +21,7 @@ def imread(fname, dtype=None): """ im = Image.open(fname) + fp = im.fp if im.mode == 'P': if _palette_is_grayscale(im): im = im.convert('L') @@ -35,8 +36,9 @@ def imread(fname, dtype=None): im.shape = shape[::-1] elif 'A' in im.mode: im = im.convert('RGBA') - - return np.array(im, dtype=dtype) + im = np.array(im, dtype=dtype) + fp.close() + return im def _palette_is_grayscale(pil_image): diff --git a/skimage/io/_plugins/qt_plugin.py b/skimage/io/_plugins/qt_plugin.py index db4cb4cc..f3a3b8e0 100644 --- a/skimage/io/_plugins/qt_plugin.py +++ b/skimage/io/_plugins/qt_plugin.py @@ -158,7 +158,7 @@ def imsave(filename, img, format_str=None): qbuffer.open(QtCore.QIODevice.ReadWrite) saved = qimg.save(qbuffer, format_str.upper()) qbuffer.seek(0) - filename.write(qbuffer.readAll()) + filename.write(qbuffer.readAll().data()) qbuffer.close() else: saved = qimg.save(filename) diff --git a/skimage/io/tests/test_imread.py b/skimage/io/tests/test_imread.py index 25ad870e..a2c32b79 100644 --- a/skimage/io/tests/test_imread.py +++ b/skimage/io/tests/test_imread.py @@ -7,6 +7,7 @@ from tempfile import NamedTemporaryFile from skimage import data_dir from skimage.io import imread, imsave, use_plugin, reset_plugins +import skimage.io as sio try: import imread as _imread @@ -39,11 +40,13 @@ def test_imread_palette(): img = imread(os.path.join(data_dir, 'palette_color.png')) assert img.ndim == 3 + @skipif(not imread_available) def test_imread_truncated_jpg(): assert_raises((RuntimeError, ValueError), sio.imread, - os.path.join(si.data_dir, 'truncated.jpg')) + os.path.join(data_dir, 'truncated.jpg')) + @skipif(not imread_available) def test_bilevel(): diff --git a/skimage/io/tests/test_simpleitk.py b/skimage/io/tests/test_simpleitk.py index ab8cd592..e7fb1e83 100644 --- a/skimage/io/tests/test_simpleitk.py +++ b/skimage/io/tests/test_simpleitk.py @@ -1,6 +1,7 @@ import os.path import numpy as np from numpy.testing.decorators import skipif +from numpy.testing import assert_raises from tempfile import NamedTemporaryFile @@ -52,12 +53,15 @@ def test_bilevel(): img = imread(os.path.join(data_dir, 'checker_bilevel.png')) np.testing.assert_array_equal(img, expected) - +""" +#TODO: This test causes a Segmentation fault @skipif(not sitk_available) def test_imread_truncated_jpg(): assert_raises((RuntimeError, ValueError), - sio.imread, - os.path.join(si.data_dir, 'truncated.jpg')) + imread, + os.path.join(data_dir, 'truncated.jpg')) +""" + @skipif(not sitk_available) def test_imread_uint16(): diff --git a/skimage/morphology/greyreconstruct.py b/skimage/morphology/greyreconstruct.py index cea200a4..bce26d6b 100644 --- a/skimage/morphology/greyreconstruct.py +++ b/skimage/morphology/greyreconstruct.py @@ -131,7 +131,7 @@ def reconstruction(seed, mask, method='dilation', selem=None, offset=None): if selem is None: selem = np.ones([3] * seed.ndim, dtype=bool) else: - selem = selem.astype(bool, copy=True) + selem = selem.astype(bool) if offset is None: if not all([d % 2 == 1 for d in selem.shape]): diff --git a/skimage/morphology/misc.py b/skimage/morphology/misc.py index 94aadf7a..edf2892b 100644 --- a/skimage/morphology/misc.py +++ b/skimage/morphology/misc.py @@ -5,12 +5,13 @@ from .selem import _default_selem # Our function names don't exactly correspond to ndimages. # This dictionary translates from our names to scipy's. funcs = ('erosion', 'dilation', 'opening', 'closing') -skimage2ndimage = {x: 'grey_' + x for x in funcs} +skimage2ndimage = dict((x, 'grey_' + x) for x in funcs) # These function names are the same in ndimage. funcs = ('binary_erosion', 'binary_dilation', 'binary_opening', 'binary_closing', 'black_tophat', 'white_tophat') -skimage2ndimage.update({x: x for x in funcs}) +skimage2ndimage.update(dict((x, x) for x in funcs)) + def default_fallback(func): """Decorator to fall back on ndimage for images with more than 2 dimensions diff --git a/skimage/restoration/tests/test_unwrap.py b/skimage/restoration/tests/test_unwrap.py index fca63724..2c2381e3 100644 --- a/skimage/restoration/tests/test_unwrap.py +++ b/skimage/restoration/tests/test_unwrap.py @@ -7,6 +7,7 @@ from numpy.testing import (run_module_suite, assert_array_almost_equal, import warnings from skimage.restoration import unwrap_phase +from skimage.util.version_requirements import require def assert_phase_almost_equal(a, b, *args, **kwargs): @@ -41,6 +42,7 @@ def check_unwrap(image, mask=None): assert_phase_almost_equal(image_unwrapped, image) +@require('python', '>=2.7') def test_unwrap_1d(): image = np.linspace(0, 10 * np.pi, 100) check_unwrap(image) @@ -50,6 +52,7 @@ def test_unwrap_1d(): assert_raises(ValueError, unwrap_phase, image, True) +@require('python', '>=2.7') def test_unwrap_2d(): x, y = np.ogrid[:8, :16] image = 2 * np.pi * (x * 0.2 + y * 0.1) @@ -59,6 +62,7 @@ def test_unwrap_2d(): yield check_unwrap, image, mask +@require('python', '>=2.7') def test_unwrap_3d(): x, y, z = np.ogrid[:8, :12, :16] image = 2 * np.pi * (x * 0.2 + y * 0.1 + z * 0.05) @@ -104,12 +108,14 @@ def check_wrap_around(ndim, axis): image_unwrap_wrap_around[index_last]) +@require('python', '>=2.7') def test_wrap_around(): for ndim in (2, 3): for axis in range(ndim): yield check_wrap_around, ndim, axis +@require('python', '>=2.7') def test_mask(): length = 100 ramps = [np.linspace(0, 4 * np.pi, length), @@ -137,6 +143,7 @@ def test_mask(): assert_array_almost_equal(image_unwrapped_3d[:, :, -1], image[i, -1]) +@require('python', '>=2.7') def test_invalid_input(): assert_raises(ValueError, unwrap_phase, np.zeros([])) assert_raises(ValueError, unwrap_phase, np.zeros((1, 1, 1, 1))) diff --git a/skimage/restoration/unwrap.py b/skimage/restoration/unwrap.py index 085bef0b..14da2677 100644 --- a/skimage/restoration/unwrap.py +++ b/skimage/restoration/unwrap.py @@ -1,12 +1,14 @@ import numpy as np import warnings from six import string_types +from skimage.util.version_requirements import require from ._unwrap_1d import unwrap_1d from ._unwrap_2d import unwrap_2d from ._unwrap_3d import unwrap_3d +@require('python', '>=2.7') def unwrap_phase(image, wrap_around=False): '''From ``image``, wrapped to lie in the interval [-pi, pi), recover the original, unwrapped image. diff --git a/skimage/util/tests/test_version_requirements.py b/skimage/util/tests/test_version_requirements.py new file mode 100644 index 00000000..765baaa8 --- /dev/null +++ b/skimage/util/tests/test_version_requirements.py @@ -0,0 +1,46 @@ +"""Tests for the version requirment functions. + +""" +import numpy as np +from numpy.testing import assert_raises, assert_equal +import nose +from skimage.util import version_requirements as vr + + +def test_get_module_version(): + assert vr.get_module_version('numpy') + assert vr.get_module_version('scipy') + assert_raises(ImportError, lambda: vr.get_module_version('fakenumpy')) + + +def test_is_installed(): + assert vr.is_installed('python', '>=2.6') + assert not vr.is_installed('numpy', '<1.0') + + +def test_require(): + + @vr.require('python', '>2.6') + @vr.require('numpy', '>1.5') + def foo(): + return 1 + + assert_equal(foo(), 1) + + @vr.require('scipy', '<0.1') + def bar(): + return 0 + + assert_raises(ImportError, lambda: bar()) + + @vr.require('numpy', '<1.0') + def test_this(): + assert False + + assert_raises(nose.SkipTest, lambda: test_this()()) + + +def test_get_module(): + assert_equal(vr.get_module('numpy'), np) + assert_equal(vr.get_module('nose'), nose) + diff --git a/skimage/util/version_requirements.py b/skimage/util/version_requirements.py new file mode 100644 index 00000000..2858aa5d --- /dev/null +++ b/skimage/util/version_requirements.py @@ -0,0 +1,125 @@ +from distutils.version import LooseVersion +import functools +import re +import sys +import types +from numpy.testing.decorators import skipif + + +def _check_version(actver, version, cmp_op): + """ + Check version string of an active module against a required version. + + If dev/prerelease tags result in TypeError for string-number comparison, + it is assumed that the dependency is satisfied. + Users on dev branches are responsible for keeping their own packages up to + date. + + Copyright (C) 2013 The IPython Development Team + + Distributed under the terms of the BSD License. + """ + try: + if cmp_op == '>': + return LooseVersion(actver) > LooseVersion(version) + elif cmp_op == '>=': + return LooseVersion(actver) >= LooseVersion(version) + elif cmp_op == '=': + return LooseVersion(actver) == LooseVersion(version) + elif cmp_op == '<': + return LooseVersion(actver) < LooseVersion(version) + else: + return False + except TypeError: + return True + + +def get_module_version(module_name): + """Return module version or None if version can't be retrieved.""" + mod = __import__(module_name, + fromlist=[module_name.rpartition('.')[-1]]) + return getattr(mod, '__version__', getattr(mod, 'VERSION', None)) + + +def is_installed(name, version=None): + """Test if *name* is installed. + + Parameters + ---------- + name : str + Name of module or "python" + version : str, optional + Version string to test against. + If version is not None, checking version + (must have an attribute named '__version__' or 'VERSION') + Version may start with =, >=, > or < to specify the exact requirement + + Note + ---- + Original Copyright (C) 2009-2011 Pierre Raybaut + Licensed under the terms of the MIT License. + + Returns + ------- + True if *name* is installed matching the optional version. + """ + if name.lower() == 'python': + actver = sys.version[:6] + else: + try: + actver = get_module_version(name) + except ImportError: + return False + if version is None: + return True + else: + match = re.search('[0-9]', version) + assert match is not None, "Invalid version number" + symb = version[:match.start()] + if not symb: + symb = '=' + assert symb in ('>=', '>', '=', '<'),\ + "Invalid version condition '%s'" % symb + version = version[match.start():] + return _check_version(actver, version, symb) + + +def require(name, version=None): + """Return decorator that forces a requirement for a function or class. + + Parameters + ---------- + name : str + Name of module or "python". + version : str, optional + Version string to test against. + If version is not None, checking version + (must have an attribute named '__version__' or 'VERSION') + Version may start with =, >=, > or < to specify the exact requirement + """ + def decorator(obj): + @functools.wraps(obj) + def func_wrapped(*args, **kwargs): + if is_installed(name, version): + return obj(*args, **kwargs) + else: + msg = '"%s" in "%s" requires "%s' + msg = msg % (obj, obj.__module__, name) + if not version is None: + msg += " %s" % version + raise ImportError(msg + '"') + if (isinstance(obj, types.FunctionType) and + obj.__name__.startswith('test_')): + if not is_installed(name, version): + func_wrapped = lambda: skipif(True)(obj) + else: + func_wrapped = obj + return func_wrapped + return decorator + + +def get_module(module_name, version=None): + if not is_installed(module_name, version): + return None + return __import__(module_name, + fromlist=[module_name.rpartition('.')[-1]]) diff --git a/skimage/viewer/__init__.py b/skimage/viewer/__init__.py index 5eed9589..a146f348 100644 --- a/skimage/viewer/__init__.py +++ b/skimage/viewer/__init__.py @@ -1 +1,11 @@ +from warnings import warn +from skimage.util.version_requirements import is_installed + from .viewers import ImageViewer, CollectionViewer +from .qt import qt_api + +viewer_available = not qt_api is None and is_installed('matplotlib') +if not viewer_available: + warn('Viewer requires matplotlib and Qt') + +del qt_api, is_installed, warn diff --git a/skimage/viewer/canvastools/base.py b/skimage/viewer/canvastools/base.py index 7f61ce2c..29e6099d 100644 --- a/skimage/viewer/canvastools/base.py +++ b/skimage/viewer/canvastools/base.py @@ -1,10 +1,8 @@ import numpy as np - try: from matplotlib import lines except ImportError: - print("Could not import matplotlib -- skimage.viewer not available.") - + pass __all__ = ['CanvasToolBase', 'ToolHandles'] diff --git a/skimage/viewer/canvastools/linetool.py b/skimage/viewer/canvastools/linetool.py index fed18383..61d94825 100644 --- a/skimage/viewer/canvastools/linetool.py +++ b/skimage/viewer/canvastools/linetool.py @@ -3,8 +3,7 @@ import numpy as np try: from matplotlib import lines except ImportError: - print("Could not import matplotlib -- skimage.viewer not available.") - + pass from skimage.viewer.canvastools.base import CanvasToolBase, ToolHandles diff --git a/skimage/viewer/canvastools/painttool.py b/skimage/viewer/canvastools/painttool.py index 6af8b5d3..fd994a10 100644 --- a/skimage/viewer/canvastools/painttool.py +++ b/skimage/viewer/canvastools/painttool.py @@ -1,9 +1,11 @@ import numpy as np -import matplotlib.pyplot as plt -import matplotlib.colors as mcolors -LABELS_CMAP = mcolors.ListedColormap(['white', 'red', 'dodgerblue', 'gold', - 'greenyellow', 'blueviolet']) - +try: + import matplotlib.pyplot as plt + import matplotlib.colors as mcolors + LABELS_CMAP = mcolors.ListedColormap(['white', 'red', 'dodgerblue', 'gold', + 'greenyellow', 'blueviolet']) +except ImportError: + pass from skimage.viewer.canvastools.base import CanvasToolBase diff --git a/skimage/viewer/canvastools/recttool.py b/skimage/viewer/canvastools/recttool.py index f055e439..929fe939 100644 --- a/skimage/viewer/canvastools/recttool.py +++ b/skimage/viewer/canvastools/recttool.py @@ -2,7 +2,6 @@ try: from matplotlib.widgets import RectangleSelector except ImportError: RectangleSelector = object - print("Could not import matplotlib -- skimage.viewer not available.") from skimage.viewer.canvastools.base import CanvasToolBase from skimage.viewer.canvastools.base import ToolHandles diff --git a/skimage/viewer/plugins/color_histogram.py b/skimage/viewer/plugins/color_histogram.py index dd54a93d..4826fc44 100644 --- a/skimage/viewer/plugins/color_histogram.py +++ b/skimage/viewer/plugins/color_histogram.py @@ -1,6 +1,8 @@ import numpy as np -import matplotlib.pyplot as plt - +try: + import matplotlib.pyplot as plt +except ImportError: + pass from skimage import color from skimage import exposure from .plotplugin import PlotPlugin diff --git a/skimage/viewer/plugins/overlayplugin.py b/skimage/viewer/plugins/overlayplugin.py index 373d6bf4..1850992d 100644 --- a/skimage/viewer/plugins/overlayplugin.py +++ b/skimage/viewer/plugins/overlayplugin.py @@ -5,17 +5,12 @@ from .base import Plugin from ..utils import ClearColormap, update_axes_image import six +from skimage.util.version_requirements import is_installed __all__ = ['OverlayPlugin'] -def recent_mpl_version(): - import matplotlib - version = matplotlib.__version__.split('.') - return int(version[0]) == 1 and int(version[1]) >= 2 - - class OverlayPlugin(Plugin): """Plugin for ImageViewer that displays an overlay on top of main image. @@ -38,14 +33,14 @@ class OverlayPlugin(Plugin): 'cyan': (0, 1, 1)} def __init__(self, **kwargs): - if not recent_mpl_version(): + if not is_installed('matplotlib', '>=1.2'): msg = "Matplotlib >= 1.2 required for OverlayPlugin." warn(RuntimeWarning(msg)) super(OverlayPlugin, self).__init__(**kwargs) self._overlay_plot = None self._overlay = None self.cmap = None - self.color_names = list(self.colors.keys()) + self.color_names = sorted(list(self.colors.keys())) def attach(self, image_viewer): super(OverlayPlugin, self).attach(image_viewer) diff --git a/skimage/viewer/tests/test_plugins.py b/skimage/viewer/tests/test_plugins.py index 7ef62730..4708db6e 100644 --- a/skimage/viewer/tests/test_plugins.py +++ b/skimage/viewer/tests/test_plugins.py @@ -4,21 +4,15 @@ import skimage import skimage.data as data from skimage.filter.rank import median from skimage.morphology import disk +from skimage.viewer import ImageViewer, viewer_available from numpy.testing import assert_equal, assert_allclose, assert_almost_equal from numpy.testing.decorators import skipif - -try: - from skimage.viewer.qt import qt_api - from skimage.viewer import ImageViewer - from skimage.viewer.plugins import ( - LineProfile, Measure, CannyPlugin, LabelPainter, Crop, ColorHistogram, - PlotPlugin) - from skimage.viewer.plugins.base import Plugin - from skimage.viewer.widgets import Slider - viewer_available = not qt_api is None -except ImportError: - viewer_available = False - +from skimage.viewer.plugins import ( + LineProfile, Measure, CannyPlugin, LabelPainter, Crop, ColorHistogram, + PlotPlugin) +from skimage.viewer.plugins.base import Plugin +from skimage.viewer.widgets import Slider +from skimage.util.version_requirements import is_installed def setup_line_profile(image, limits='image'): viewer = ImageViewer(skimage.img_as_float(image)) diff --git a/skimage/viewer/tests/test_tools.py b/skimage/viewer/tests/test_tools.py index 9f02e762..6663c465 100644 --- a/skimage/viewer/tests/test_tools.py +++ b/skimage/viewer/tests/test_tools.py @@ -1,19 +1,15 @@ from collections import namedtuple import numpy as np -from skimage import data from numpy.testing import assert_equal from numpy.testing.decorators import skipif - -try: - from skimage.viewer.qt import qt_api - from skimage.viewer import ImageViewer - from skimage.viewer.canvastools import ( - LineTool, ThickLineTool, RectangleTool, PaintTool) - from skimage.viewer.canvastools.base import CanvasToolBase - viewer_available = not qt_api is None -except ImportError: - viewer_available = False +from skimage import data +from skimage.viewer import ImageViewer, viewer_available +from skimage.viewer.canvastools import ( + LineTool, ThickLineTool, RectangleTool, PaintTool) +from skimage.viewer.canvastools.base import CanvasToolBase +from numpy.testing import assert_equal +from numpy.testing.decorators import skipif def get_end_points(image): diff --git a/skimage/viewer/tests/test_utils.py b/skimage/viewer/tests/test_utils.py index 30d74a51..3f9ec396 100644 --- a/skimage/viewer/tests/test_utils.py +++ b/skimage/viewer/tests/test_utils.py @@ -1,15 +1,14 @@ # -*- coding: utf-8 -*- +from skimage.viewer import viewer_available +from skimage.viewer.qt import QtCore, QtGui +from skimage.viewer import utils +from skimage.viewer.utils import dialogs from numpy.testing.decorators import skipif - -try: - from skimage.viewer.qt import qt_api, QtCore, QtGui - from skimage.viewer import utils - from skimage.viewer.utils import dialogs -except ImportError: - qt_api = None +from skimage.viewer import utils +from skimage.viewer.utils import dialogs -@skipif(qt_api is None) +@skipif(not viewer_available) def test_event_loop(): utils.init_qtapp() timer = QtCore.QTimer() @@ -17,7 +16,7 @@ def test_event_loop(): utils.start_qtapp() -@skipif(qt_api is None) +@skipif(not viewer_available) def test_format_filename(): fname = dialogs._format_filename(('apple', 2)) assert fname == 'apple' @@ -25,7 +24,7 @@ def test_format_filename(): assert fname is None -@skipif(qt_api is None) +@skipif(not viewer_available) def test_open_file_dialog(): utils.init_qtapp() timer = QtCore.QTimer() @@ -34,7 +33,7 @@ def test_open_file_dialog(): assert filename is None -@skipif(qt_api is None) +@skipif(not viewer_available) def test_save_file_dialog(): utils.init_qtapp() timer = QtCore.QTimer() diff --git a/skimage/viewer/tests/test_viewer.py b/skimage/viewer/tests/test_viewer.py index cd6c7acb..85a0d76f 100644 --- a/skimage/viewer/tests/test_viewer.py +++ b/skimage/viewer/tests/test_viewer.py @@ -1,18 +1,13 @@ from skimage import data +from skimage.viewer.qt import QtGui, QtCore +from skimage.viewer import ImageViewer, CollectionViewer, viewer_available from skimage.transform import pyramid_gaussian +from skimage.viewer.plugins import OverlayPlugin from skimage.filter import sobel from numpy.testing import assert_equal from numpy.testing.decorators import skipif - -try: - from skimage.viewer.qt import qt_api, QtGui, QtCore - from skimage.viewer.plugins import OverlayPlugin - from skimage.viewer.plugins.overlayplugin import recent_mpl_version - from skimage.viewer import ImageViewer, CollectionViewer - viewer_available = not qt_api is None -except ImportError: - viewer_available = False +from skimage.util.version_requirements import require @skipif(not viewer_available) @@ -58,7 +53,8 @@ def test_collection_viewer(): view._format_coord(10, 10) -@skipif(not viewer_available or not recent_mpl_version()) +@skipif(not viewer_available) +@require('matplotlib', '>=1.2') def test_viewer_with_overlay(): img = data.coins() ov = OverlayPlugin(image_filter=sobel) @@ -68,7 +64,7 @@ def test_viewer_with_overlay(): import tempfile _, filename = tempfile.mkstemp(suffix='.png') - ov.color = 2 + ov.color = 3 assert_equal(ov.color, 'yellow') viewer.save_to_file(filename) ov.display_filtered_image(img) @@ -79,5 +75,3 @@ def test_viewer_with_overlay(): assert_equal(ov.overlay, img) assert_equal(ov.filtered_image, img) - - diff --git a/skimage/viewer/tests/test_widgets.py b/skimage/viewer/tests/test_widgets.py index aeb4ede2..ef3cb83c 100644 --- a/skimage/viewer/tests/test_widgets.py +++ b/skimage/viewer/tests/test_widgets.py @@ -1,19 +1,14 @@ import os from skimage import data, img_as_float, io +from skimage.viewer import ImageViewer, viewer_available +from skimage.viewer.widgets import ( + Slider, OKCancelButtons, SaveButtons, ComboBox, Text) +from skimage.viewer.plugins.base import Plugin +from skimage.viewer.qt import QtGui, QtCore from numpy.testing import assert_almost_equal, assert_equal from numpy.testing.decorators import skipif -try: - from skimage.viewer.qt import qt_api, QtGui, QtCore - from skimage.viewer import ImageViewer - from skimage.viewer.widgets import ( - Slider, OKCancelButtons, SaveButtons, ComboBox, Text) - from skimage.viewer.plugins.base import Plugin - viewer_available = not qt_api is None -except ImportError: - viewer_available = False - def get_image_viewer(): image = data.coins() diff --git a/skimage/viewer/utils/core.py b/skimage/viewer/utils/core.py index 95176fcd..19a5c73d 100644 --- a/skimage/viewer/utils/core.py +++ b/skimage/viewer/utils/core.py @@ -9,18 +9,15 @@ try: from matplotlib.figure import Figure from matplotlib import _pylab_helpers from matplotlib.colors import LinearSegmentedColormap - if qt_api is None: - raise ImportError - else: + if not qt_api is None: from matplotlib.backends.backend_qt4 import FigureManagerQT from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg - if 'agg' not in mpl.get_backend().lower(): - print("Recommended matplotlib backend is `Agg` for full " - "skimage.viewer functionality.") + if 'agg' not in mpl.get_backend().lower(): + print("Recommended matplotlib backend is `Agg` for full " + "skimage.viewer functionality.") except ImportError: FigureCanvasQTAgg = object # hack to prevent nosetest and autodoc errors LinearSegmentedColormap = object - print("Could not import matplotlib -- skimage.viewer not available.") from ..qt import QtGui diff --git a/skimage/viewer/viewers/core.py b/skimage/viewer/viewers/core.py index 476017ef..52ac073a 100644 --- a/skimage/viewer/viewers/core.py +++ b/skimage/viewer/viewers/core.py @@ -123,7 +123,6 @@ class ImageViewer(QtGui.QMainWindow): self.fig, self.ax = utils.figimage(image) self.canvas = self.fig.canvas self.canvas.setParent(self) - self.ax.autoscale(enable=False) self._image_plot = self.ax.images[0] diff --git a/tools/build_versions.py b/tools/build_versions.py index 2c0e32ba..5e33dbfa 100755 --- a/tools/build_versions.py +++ b/tools/build_versions.py @@ -4,9 +4,12 @@ from __future__ import print_function import numpy as np import scipy as sp -import matplotlib as mpl +from PIL import Image import six -for m in (np, sp, mpl, six): - print(m.__name__.rjust(10), ' ', m.__version__) - +for m in (np, sp, Image, six): + if not m is None: + if m is Image: + print('PIL'.rjust(10), ' ', Image.VERSION) + else: + print(m.__name__.rjust(10), ' ', m.__version__) From e3b84edf1ba1e36dd191bcb0b4b802514fb91ee9 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 2 Aug 2014 10:30:32 -0500 Subject: [PATCH 02/11] Fix python 2.6 restoration/unwrap error --- .travis.yml | 7 +------ skimage/restoration/tests/test_unwrap.py | 7 ------- skimage/restoration/unwrap.py | 3 +-- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1ef5b85d..486c5b60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -90,18 +90,13 @@ script: # Run all doc examples - export PYTHONPATH=$(pwd):$PYTHONPATH - - if [[ $ENV != python=2.6* ]]; then - for f in doc/examples/*.py; do python "$f"; if [ $? -ne 0 ]; then exit 1; fi done - fi + - for f in doc/examples/*.py; do python "$f"; if [ $? -ne 0 ]; then exit 1; fi done - for f in doc/examples/applications/*.py; do python "$f"; if [ $? -ne 0 ]; then exit 1; fi done # run tests again with optional dependencies to get more coverage # measure coverage on py3.3 - # doctests fail on python 2.6 for restoration/unwrap - if [[ $ENV == python=3.3* ]]; then nosetests --exe -v --with-doctest --with-cov --cover-package skimage; - elif [[ $ENV == python=2.6* ]]; then - nosetests --exe -v skimage; else nosetests --exe -v --with-doctest skimage; fi diff --git a/skimage/restoration/tests/test_unwrap.py b/skimage/restoration/tests/test_unwrap.py index 2c2381e3..fca63724 100644 --- a/skimage/restoration/tests/test_unwrap.py +++ b/skimage/restoration/tests/test_unwrap.py @@ -7,7 +7,6 @@ from numpy.testing import (run_module_suite, assert_array_almost_equal, import warnings from skimage.restoration import unwrap_phase -from skimage.util.version_requirements import require def assert_phase_almost_equal(a, b, *args, **kwargs): @@ -42,7 +41,6 @@ def check_unwrap(image, mask=None): assert_phase_almost_equal(image_unwrapped, image) -@require('python', '>=2.7') def test_unwrap_1d(): image = np.linspace(0, 10 * np.pi, 100) check_unwrap(image) @@ -52,7 +50,6 @@ def test_unwrap_1d(): assert_raises(ValueError, unwrap_phase, image, True) -@require('python', '>=2.7') def test_unwrap_2d(): x, y = np.ogrid[:8, :16] image = 2 * np.pi * (x * 0.2 + y * 0.1) @@ -62,7 +59,6 @@ def test_unwrap_2d(): yield check_unwrap, image, mask -@require('python', '>=2.7') def test_unwrap_3d(): x, y, z = np.ogrid[:8, :12, :16] image = 2 * np.pi * (x * 0.2 + y * 0.1 + z * 0.05) @@ -108,14 +104,12 @@ def check_wrap_around(ndim, axis): image_unwrap_wrap_around[index_last]) -@require('python', '>=2.7') def test_wrap_around(): for ndim in (2, 3): for axis in range(ndim): yield check_wrap_around, ndim, axis -@require('python', '>=2.7') def test_mask(): length = 100 ramps = [np.linspace(0, 4 * np.pi, length), @@ -143,7 +137,6 @@ def test_mask(): assert_array_almost_equal(image_unwrapped_3d[:, :, -1], image[i, -1]) -@require('python', '>=2.7') def test_invalid_input(): assert_raises(ValueError, unwrap_phase, np.zeros([])) assert_raises(ValueError, unwrap_phase, np.zeros((1, 1, 1, 1))) diff --git a/skimage/restoration/unwrap.py b/skimage/restoration/unwrap.py index 14da2677..f0025775 100644 --- a/skimage/restoration/unwrap.py +++ b/skimage/restoration/unwrap.py @@ -1,14 +1,12 @@ import numpy as np import warnings from six import string_types -from skimage.util.version_requirements import require from ._unwrap_1d import unwrap_1d from ._unwrap_2d import unwrap_2d from ._unwrap_3d import unwrap_3d -@require('python', '>=2.7') def unwrap_phase(image, wrap_around=False): '''From ``image``, wrapped to lie in the interval [-pi, pi), recover the original, unwrapped image. @@ -86,6 +84,7 @@ def unwrap_phase(image, wrap_around=False): if np.ma.isMaskedArray(image): mask = np.require(image.mask, np.uint8, ['C']) + image = image.data else: mask = np.zeros_like(image, dtype=np.uint8, order='C') image_not_masked = np.asarray(image, dtype=np.float64, order='C') From 208afc38371941f257c2b713481f1f92486654c0 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 2 Aug 2014 11:09:46 -0500 Subject: [PATCH 03/11] Move version_requirements to _shared and remove skipif from require. --- .../{util => _shared}/tests/test_version_requirements.py | 8 +------- skimage/{util => _shared}/version_requirements.py | 7 ------- skimage/graph/tests/test_rag.py | 7 ++++--- skimage/viewer/__init__.py | 2 +- skimage/viewer/plugins/overlayplugin.py | 2 +- skimage/viewer/tests/test_plugins.py | 2 +- skimage/viewer/tests/test_viewer.py | 4 ++-- 7 files changed, 10 insertions(+), 22 deletions(-) rename skimage/{util => _shared}/tests/test_version_requirements.py (81%) rename skimage/{util => _shared}/version_requirements.py (92%) diff --git a/skimage/util/tests/test_version_requirements.py b/skimage/_shared/tests/test_version_requirements.py similarity index 81% rename from skimage/util/tests/test_version_requirements.py rename to skimage/_shared/tests/test_version_requirements.py index 765baaa8..9511a1fe 100644 --- a/skimage/util/tests/test_version_requirements.py +++ b/skimage/_shared/tests/test_version_requirements.py @@ -4,7 +4,7 @@ import numpy as np from numpy.testing import assert_raises, assert_equal import nose -from skimage.util import version_requirements as vr +from skimage._shared import version_requirements as vr def test_get_module_version(): @@ -33,12 +33,6 @@ def test_require(): assert_raises(ImportError, lambda: bar()) - @vr.require('numpy', '<1.0') - def test_this(): - assert False - - assert_raises(nose.SkipTest, lambda: test_this()()) - def test_get_module(): assert_equal(vr.get_module('numpy'), np) diff --git a/skimage/util/version_requirements.py b/skimage/_shared/version_requirements.py similarity index 92% rename from skimage/util/version_requirements.py rename to skimage/_shared/version_requirements.py index 2858aa5d..7407d600 100644 --- a/skimage/util/version_requirements.py +++ b/skimage/_shared/version_requirements.py @@ -3,7 +3,6 @@ import functools import re import sys import types -from numpy.testing.decorators import skipif def _check_version(actver, version, cmp_op): @@ -108,12 +107,6 @@ def require(name, version=None): if not version is None: msg += " %s" % version raise ImportError(msg + '"') - if (isinstance(obj, types.FunctionType) and - obj.__name__.startswith('test_')): - if not is_installed(name, version): - func_wrapped = lambda: skipif(True)(obj) - else: - func_wrapped = obj return func_wrapped return decorator diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index b6f95cb2..753bcb74 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -1,6 +1,7 @@ import numpy as np from skimage import graph -from skimage.util.version_requirements import require +from skimage._shared.version_requirements import is_installed +from numpy.testing.decorators import skipif def max_edge(g, src, dst, n): @@ -10,7 +11,7 @@ def max_edge(g, src, dst, n): return max(w1, w2) -@require('networkx') +@skipif(not is_installed('networkx')) def test_rag_merge(): g = graph.rag.RAG() @@ -45,7 +46,7 @@ def test_rag_merge(): assert g.edges() == [] -@require('networkx') +@skipif(not is_installed('networkx')) def test_threshold_cut(): img = np.zeros((100, 100, 3), dtype='uint8') diff --git a/skimage/viewer/__init__.py b/skimage/viewer/__init__.py index a146f348..4bf70a93 100644 --- a/skimage/viewer/__init__.py +++ b/skimage/viewer/__init__.py @@ -1,5 +1,5 @@ from warnings import warn -from skimage.util.version_requirements import is_installed +from skimage._shared.version_requirements import is_installed from .viewers import ImageViewer, CollectionViewer from .qt import qt_api diff --git a/skimage/viewer/plugins/overlayplugin.py b/skimage/viewer/plugins/overlayplugin.py index 1850992d..7f016328 100644 --- a/skimage/viewer/plugins/overlayplugin.py +++ b/skimage/viewer/plugins/overlayplugin.py @@ -5,7 +5,7 @@ from .base import Plugin from ..utils import ClearColormap, update_axes_image import six -from skimage.util.version_requirements import is_installed +from skimage._shared.version_requirements import is_installed __all__ = ['OverlayPlugin'] diff --git a/skimage/viewer/tests/test_plugins.py b/skimage/viewer/tests/test_plugins.py index 4708db6e..497708a4 100644 --- a/skimage/viewer/tests/test_plugins.py +++ b/skimage/viewer/tests/test_plugins.py @@ -12,7 +12,7 @@ from skimage.viewer.plugins import ( PlotPlugin) from skimage.viewer.plugins.base import Plugin from skimage.viewer.widgets import Slider -from skimage.util.version_requirements import is_installed + def setup_line_profile(image, limits='image'): viewer = ImageViewer(skimage.img_as_float(image)) diff --git a/skimage/viewer/tests/test_viewer.py b/skimage/viewer/tests/test_viewer.py index 85a0d76f..1b93ff52 100644 --- a/skimage/viewer/tests/test_viewer.py +++ b/skimage/viewer/tests/test_viewer.py @@ -7,7 +7,7 @@ from skimage.viewer.plugins import OverlayPlugin from skimage.filter import sobel from numpy.testing import assert_equal from numpy.testing.decorators import skipif -from skimage.util.version_requirements import require +from skimage._shared.version_requirements import is_installed @skipif(not viewer_available) @@ -54,7 +54,7 @@ def test_collection_viewer(): @skipif(not viewer_available) -@require('matplotlib', '>=1.2') +@skipif(not is_installed('matplotlib', '>=1.2')) def test_viewer_with_overlay(): img = data.coins() ov = OverlayPlugin(image_filter=sobel) From f2b7e104f8b1056fef70c328d43c7b9c1f55bd82 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 4 Aug 2014 06:37:48 -0500 Subject: [PATCH 04/11] Add documentation and rename import variable --- .travis.yml | 2 ++ .../tests/test_version_requirements.py | 26 ++++++++++--------- skimage/_shared/version_requirements.py | 20 ++++++++++++++ 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 486c5b60..4633a395 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,8 @@ before_install: - sh -e /etc/init.d/xvfb start - sudo apt-get update + # Python 3.2 is not supported by Miniconda, so we use the package manager for that run. + # NumPy has a bug in python 3 that is only fixed in the latest version, hence the download of _import _tools. - if [[ $ENV == python=3.2 ]]; then sudo apt-get install python3-numpy; wget https://raw.githubusercontent.com/numpy/numpy/master/numpy/_import_tools.py -O /home/travis/virtualenv/python3.2_with_system_site_packages/lib/python3.2/site-packages/numpy/_import_tools.py; diff --git a/skimage/_shared/tests/test_version_requirements.py b/skimage/_shared/tests/test_version_requirements.py index 9511a1fe..7c9bd2fb 100644 --- a/skimage/_shared/tests/test_version_requirements.py +++ b/skimage/_shared/tests/test_version_requirements.py @@ -4,30 +4,33 @@ import numpy as np from numpy.testing import assert_raises, assert_equal import nose -from skimage._shared import version_requirements as vr +from skimage._shared import version_requirements as version_req def test_get_module_version(): - assert vr.get_module_version('numpy') - assert vr.get_module_version('scipy') - assert_raises(ImportError, lambda: vr.get_module_version('fakenumpy')) + assert version_req.get_module_version('numpy') + assert version_req.get_module_version('scipy') + assert_raises(ImportError, + lambda: version_req.get_module_version('fakenumpy')) def test_is_installed(): - assert vr.is_installed('python', '>=2.6') - assert not vr.is_installed('numpy', '<1.0') + assert version_req.is_installed('python', '>=2.6') + assert not version_req.is_installed('numpy', '<1.0') def test_require(): - @vr.require('python', '>2.6') - @vr.require('numpy', '>1.5') + # A function that only runs on Python >2.6 and numpy > 1.5 (should pass) + @version_req.require('python', '>2.6') + @version_req.require('numpy', '>1.5') def foo(): return 1 assert_equal(foo(), 1) - @vr.require('scipy', '<0.1') + # function that requires scipy < 0.1 (should fail) + @version_req.require('scipy', '<0.1') def bar(): return 0 @@ -35,6 +38,5 @@ def test_require(): def test_get_module(): - assert_equal(vr.get_module('numpy'), np) - assert_equal(vr.get_module('nose'), nose) - + assert_equal(version_req.get_module('numpy'), np) + assert_equal(version_req.get_module('nose'), nose) diff --git a/skimage/_shared/version_requirements.py b/skimage/_shared/version_requirements.py index 7407d600..1da9c195 100644 --- a/skimage/_shared/version_requirements.py +++ b/skimage/_shared/version_requirements.py @@ -95,6 +95,10 @@ def require(name, version=None): If version is not None, checking version (must have an attribute named '__version__' or 'VERSION') Version may start with =, >=, > or < to specify the exact requirement + + Returns + ------- + A decorator function. """ def decorator(obj): @functools.wraps(obj) @@ -112,6 +116,22 @@ def require(name, version=None): def get_module(module_name, version=None): + """Return a module object of name *module_name* if installed. + + Parameters + ---------- + module_name : str + Name of module. + version : str, optional + Version string to test against. + If version is not None, checking version + (must have an attribute named '__version__' or 'VERSION') + Version may start with =, >=, > or < to specify the exact requirement + + Returns + ------- + Module if *module_name* is installed matching the optional version or None. + """ if not is_installed(module_name, version): return None return __import__(module_name, From 26a550a93b44ac29e0bc2b78b307048cadbc6f58 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 4 Aug 2014 07:13:42 -0500 Subject: [PATCH 05/11] Formatting fix. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4633a395..e21fd201 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ before_install: - sudo apt-get update # Python 3.2 is not supported by Miniconda, so we use the package manager for that run. - # NumPy has a bug in python 3 that is only fixed in the latest version, hence the download of _import _tools. + # NumPy has a bug in python 3 that is only fixed in the latest version, hence the download of _import_tools. - if [[ $ENV == python=3.2 ]]; then sudo apt-get install python3-numpy; wget https://raw.githubusercontent.com/numpy/numpy/master/numpy/_import_tools.py -O /home/travis/virtualenv/python3.2_with_system_site_packages/lib/python3.2/site-packages/numpy/_import_tools.py; From 382be1a45789d85ef27bc85c5fb1e07b206242c3 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 4 Aug 2014 07:26:07 -0500 Subject: [PATCH 06/11] Formatting and docstring cleanup. --- .../_shared/tests/test_version_requirements.py | 2 +- skimage/_shared/version_requirements.py | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/skimage/_shared/tests/test_version_requirements.py b/skimage/_shared/tests/test_version_requirements.py index 7c9bd2fb..59cf5814 100644 --- a/skimage/_shared/tests/test_version_requirements.py +++ b/skimage/_shared/tests/test_version_requirements.py @@ -1,4 +1,4 @@ -"""Tests for the version requirment functions. +"""Tests for the version requirement functions. """ import numpy as np diff --git a/skimage/_shared/version_requirements.py b/skimage/_shared/version_requirements.py index 1da9c195..4ebf8ee9 100644 --- a/skimage/_shared/version_requirements.py +++ b/skimage/_shared/version_requirements.py @@ -2,7 +2,6 @@ from distutils.version import LooseVersion import functools import re import sys -import types def _check_version(actver, version, cmp_op): @@ -53,14 +52,15 @@ def is_installed(name, version=None): (must have an attribute named '__version__' or 'VERSION') Version may start with =, >=, > or < to specify the exact requirement + Returns + ------- + out : bool + True if *name* is installed matching the optional version. + Note ---- Original Copyright (C) 2009-2011 Pierre Raybaut Licensed under the terms of the MIT License. - - Returns - ------- - True if *name* is installed matching the optional version. """ if name.lower() == 'python': actver = sys.version[:6] @@ -98,7 +98,9 @@ def require(name, version=None): Returns ------- - A decorator function. + func : function + A decorator that raises an ImportError if a function is run + in the absence of the input dependency. """ def decorator(obj): @functools.wraps(obj) @@ -130,7 +132,9 @@ def get_module(module_name, version=None): Returns ------- - Module if *module_name* is installed matching the optional version or None. + mod : module or None + Module if *module_name* is installed matching the optional version + or None otherwise. """ if not is_installed(module_name, version): return None From 04e53f124f4aecf71320db44ee65d1baf384d24e Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 4 Aug 2014 07:30:13 -0500 Subject: [PATCH 07/11] Remove confusing dummy variable --- skimage/graph/graph_cut.py | 1 - 1 file changed, 1 deletion(-) diff --git a/skimage/graph/graph_cut.py b/skimage/graph/graph_cut.py index 443b1561..d9fcfdef 100644 --- a/skimage/graph/graph_cut.py +++ b/skimage/graph/graph_cut.py @@ -3,7 +3,6 @@ try: except ImportError: import warnings warnings.warn('"cut_threshold" requires networkx') - nx = None import numpy as np From 56152fe6a787139654b060bf7603d30584f0e5b5 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 4 Aug 2014 07:32:11 -0500 Subject: [PATCH 08/11] Update comment --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e21fd201..5f127645 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,8 @@ before_install: - sudo apt-get update # Python 3.2 is not supported by Miniconda, so we use the package manager for that run. - # NumPy has a bug in python 3 that is only fixed in the latest version, hence the download of _import_tools. + # NumPy has a bug in python 3 that is only fixed in the latest version, + # hence the below wget of numpy/_import_tools.py from github. - if [[ $ENV == python=3.2 ]]; then sudo apt-get install python3-numpy; wget https://raw.githubusercontent.com/numpy/numpy/master/numpy/_import_tools.py -O /home/travis/virtualenv/python3.2_with_system_site_packages/lib/python3.2/site-packages/numpy/_import_tools.py; From 2a339fe69cbca2a871b5b2425caee417d462a928 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 4 Aug 2014 07:40:37 -0500 Subject: [PATCH 09/11] Improve import behavior for rag fallback --- skimage/graph/rag.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index fc4a1aaa..7c1b7f07 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -1,11 +1,13 @@ try: import networkx as nx except ImportError: + msg = "Graph functions require networkx, which is not installed" class nx: class Graph: - pass + def __init__(self, *args, **kwargs): + raise ImportError(msg) import warnings - warnings.warn('Region Adjacency Graph (RAG) features require networkx') + warnings.warn(msg) import numpy as np from scipy.ndimage import filters from scipy import ndimage as nd From 480a9f72fc0326840394310507f3ca284e4c828e Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 4 Aug 2014 10:44:23 -0500 Subject: [PATCH 10/11] Add note in travis --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5f127645..b0acc3c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,8 @@ language: python + +# Tthe Travis python is set to 3.2 for all builds, since we use the default python for the 3.2 build and the anaconda python otherwise python: - 3.2 @@ -24,7 +26,7 @@ before_install: - sudo apt-get update # Python 3.2 is not supported by Miniconda, so we use the package manager for that run. - # NumPy has a bug in python 3 that is only fixed in the latest version, + # NumPy has a bug in python 3 that is only fixed in the latest version, # hence the below wget of numpy/_import_tools.py from github. - if [[ $ENV == python=3.2 ]]; then sudo apt-get install python3-numpy; @@ -59,7 +61,7 @@ script: - flake8 --exit-zero --exclude=test_*,six.py skimage doc/examples viewer_examples # Install optional dependencies to get full test coverage - # Notes: + # Notes: # - pyfits and imread do NOT support py3.2 # - Use the png headers included in anaconda (from matplotlib install) # TODO: Remove the libm removal when anaconda fixes their libraries From 5996cf8abacddf22af7e0523cca9f1c4be0b46c7 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 4 Aug 2014 10:48:15 -0500 Subject: [PATCH 11/11] Add travis_retry around the conda installs --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b0acc3c4..3a061e13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,7 +41,7 @@ before_install: conda config --set always_yes yes; conda update conda; conda info -a; - conda create -n test $ENV six scipy pip flake8 nose; + travis_retry conda create -n test $ENV six scipy pip flake8 nose; source activate test; fi - pip install coveralls pillow @@ -72,7 +72,7 @@ script: pip install --use-mirrors -q matplotlib; pip install -q networkx; else - conda install -q matplotlib pyqt networkx; + travis_retry conda install -q matplotlib pyqt networkx; sudo cp ~/miniconda/envs/test/include/png* /usr/include; rm ~/miniconda/envs/test/lib/libm-2.5.so; rm ~/miniconda/envs/test/lib/libm.*;