Skip to content

Commit

Permalink
Merge pull request #4668 from jenshnielsen/setuppy_test_deps
Browse files Browse the repository at this point in the history
TST: Remove test dependencies from install_requires
  • Loading branch information
tacaswell committed Jul 14, 2015
2 parents beaca96 + f757166 commit 6cd1ddd
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 32 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -35,6 +35,7 @@ doc/_build
dist
# Egg metadata
*.egg-info
.eggs
# tox testing tool
.tox

Expand Down
8 changes: 3 additions & 5 deletions .travis.yml
Expand Up @@ -25,9 +25,9 @@ env:
- secure: E7OCdqhZ+PlwJcn+Hd6ns9TDJgEUXiUNEI0wu7xjxB2vBRRIKtZMbuaZjd+iKDqCKuVOJKu0ClBUYxmgmpLicTwi34CfTUYt6D4uhrU+8hBBOn1iiK51cl/aBvlUUrqaRLVhukNEBGZcyqAjXSA/Qsnp2iELEmAfOUa92ZYo1sk=
- secure: "dfjNqGKzQG5bu3FnDNwLG8H/C4QoieFo4PfFmZPdM2RY7WIzukwKFNT6kiDfOrpwt+2bR7FhzjOGlDECGtlGOtYPN8XuXGjhcP4a4IfakdbDfF+D3NPIpf5VlE6776k0VpvcZBTMYJKNFIMc7QPkOwjvNJ2aXyfe3hBuGlKJzQU="
- BUILD_DOCS=false
- TEST_ARGS=--no-pep8
- NUMPY=numpy
- NPROC=2
- TEST_ARGS=--omit-pep8

language: python

Expand All @@ -40,7 +40,7 @@ matrix:
- python: 3.3
- python: 3.4
- python: 2.7
env: TEST_ARGS=--pep8
env: TEST_ARGS=--pep8-only
- python: 2.7
env: BUILD_DOCS=true MOCK=mock
- python: "nightly"
Expand Down Expand Up @@ -109,9 +109,7 @@ script:
- |
if [[ $BUILD_DOCS == false ]]; then
export MPL_REPO_DIR=$PWD # needed for pep8-conformance test of the examples
mkdir ../tmp_test_dir
cd ../tmp_test_dir
gdb -return-child-result -batch -ex r -ex bt --args python ../matplotlib/tests.py -s --processes=$NPROC --process-timeout=300 $TEST_ARGS
gdb -return-child-result -batch -ex r -ex bt --args python setup.py test --nocapture --processes=$NPROC --process-timeout=300 $TEST_ARGS
else
cd doc
python make.py html --small --warningsaserrors
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Expand Up @@ -45,10 +45,10 @@ jdh_doc_snapshot:


test:
${PYTHON} tests.py
${PYTHON} setup.py test


test-coverage:
${PYTHON} tests.py --with-coverage --cover-package=matplotlib
${PYTHON} setup.py test --with-coverage --cover-package=matplotlib


7 changes: 6 additions & 1 deletion README.rst
Expand Up @@ -20,7 +20,12 @@ Testing

After installation, you can launch the test suite::

python tests.py
python setup.py test

Or from the python interpreter::

import matplotlib
matplotlib.test()

Consider reading http://matplotlib.org/devel/coding_guide.html#testing for
more information.
4 changes: 2 additions & 2 deletions doc/devel/release_guide.rst
Expand Up @@ -13,8 +13,8 @@ A guide for developers who are doing a matplotlib release.
Testing
=======

* Run all of the regression tests by running the `tests.py` script at
the root of the source tree.
* Run all of the regression tests by running ``python setup.py test`` script
at the root of the source tree.

* Run :file:`unit/memleak_hawaii3.py` and make sure there are no
memory leaks
Expand Down
48 changes: 30 additions & 18 deletions doc/devel/testing.rst
Expand Up @@ -37,14 +37,24 @@ Running the tests
-----------------

Running the tests is simple. Make sure you have nose installed and run
the script :file:`tests.py` in the root directory of the distribution.
The script can take any of the usual `nosetest arguments`_, such as
the setup script's ``test`` command::

=================== ===========
``-v`` increase verbosity
``-d`` detailed error messages
``--with-coverage`` enable collecting coverage information
=================== ===========
python setup.py test

in the root directory of the distribution. The script takes a set of
commands, such as:

======================== ===========
``--pep8-only`` pep8 checks
``--omit-pep8`` Do not perform pep8 checks
``--nocapture`` do not capture stdout (nosetests)
``--nose-verbose`` be verbose (nosetests)
``--processes`` number of processes (nosetests)
``--process-timeout`` process timeout (nosetests)
``--with-coverage`` with coverage
``--detailed-error-msg`` detailed error message (nosetest)
``--tests`` comma separated selection of tests (nosetest)
======================== ===========

Additionally it is possible to run only coding standard test or disable them:

Expand All @@ -57,28 +67,30 @@ To run a single test from the command line, you can provide a
dot-separated path to the module followed by the function separated by
a colon, e.g., (this is assuming the test is installed)::

python tests.py matplotlib.tests.test_simplification:test_clipping

If you want to run the full test suite, but want to save wall time try running the
tests in parallel::
python setup.py test --tests=matplotlib.tests.test_simplification:test_clipping

python ../matplotlib/tests.py -sv --processes=5 --process-timeout=300
If you want to run the full test suite, but want to save wall time try
running the tests in parallel::

as we do on Travis.ci.
python setup.py test --nocapture --nose-verbose --processes=5 --process-timeout=300


An alternative implementation that does not look at command line
arguments works from within Python::
arguments works from within Python is to run the tests from the
matplotlib library function :func:`matplotlib.test`::

import matplotlib
matplotlib.test()

.. _`nosetest arguments`: http://nose.readthedocs.org/en/latest/usage.html
.. hint::

You might need to install nose for this::

pip install nose


Running tests by any means other than `matplotlib.test()`
does not load the nose "knownfailureif" (Known failing tests) plugin,
causing known-failing tests to fail for real.
.. _`nosetest arguments`: http://nose.readthedocs.org/en/latest/usage.html


Writing a simple test
---------------------
Expand Down
13 changes: 13 additions & 0 deletions lib/matplotlib/__init__.py
Expand Up @@ -1449,8 +1449,21 @@ def tk_window_focus():
]


def verify_test_dependencies():
try:
import nose
try:
from unittest import mock
except:
import mock
except ImportError:
print("matplotlib.test requires nose and mock to run.")
raise


def test(verbosity=1):
"""run the matplotlib test suite"""
verify_test_dependencies()
try:
import faulthandler
except ImportError:
Expand Down
117 changes: 117 additions & 0 deletions setup.py
Expand Up @@ -8,6 +8,7 @@
# This needs to be the very first thing to use distribute
from distribute_setup import use_setuptools
use_setuptools()
from setuptools.command.test import test as TestCommand

import sys

Expand Down Expand Up @@ -121,6 +122,118 @@
'Topic :: Scientific/Engineering :: Visualization',
]


class NoseTestCommand(TestCommand):
"""Invoke unit tests using nose after an in-place build."""

description = "Invoke unit tests using nose after an in-place build."
user_options = [
("pep8-only", None, "pep8 checks"),
("omit-pep8", None, "Do not perform pep8 checks"),
("nocapture", None, "do not capture stdout (nosetests)"),
("nose-verbose", None, "be verbose (nosetests)"),
("processes=", None, "number of processes (nosetests)"),
("process-timeout=", None, "process timeout (nosetests)"),
("with-coverage", None, "with coverage"),
("detailed-error-msg", None, "detailed error message (nosetest)"),
("tests=", None, "comma separated selection of tests (nosetest)"),
]

def initialize_options(self):
self.pep8_only = None
self.omit_pep8 = None

# parameters passed to nose tests
self.processes = None
self.process_timeout = None
self.nose_verbose = None
self.nocapture = None
self.with_coverage = None
self.detailed_error_msg = None
self.tests = None

def finalize_options(self):
self.test_args = []
if self.pep8_only:
self.pep8_only = True
if self.omit_pep8:
self.omit_pep8 = True

if self.pep8_only and self.omit_pep8:
from distutils.errors import DistutilsOptionError
raise DistutilsOptionError(
"You are using several options for the test command in an "
"incompatible manner. Please use either --pep8-only or "
"--omit-pep8"
)

if self.processes:
self.test_args.append("--processes={prc}".format(
prc=self.processes))

if self.process_timeout:
self.test_args.append("--process-timeout={tout}".format(
tout=self.process_timeout))

if self.nose_verbose:
self.test_args.append("--verbose")

if self.nocapture:
self.test_args.append("--nocapture")

if self.with_coverage:
self.test_args.append("--with-coverage")

if self.detailed_error_msg:
self.test_args.append("-d")

if self.tests:
self.test_args.append("--tests={names}".format(names=self.tests))


def run(self):
if self.distribution.install_requires:
self.distribution.fetch_build_eggs(
self.distribution.install_requires)
if self.distribution.tests_require:
self.distribution.fetch_build_eggs(self.distribution.tests_require)

self.announce('running unittests with nose')
self.with_project_on_sys_path(self.run_tests)


def run_tests(self):
import matplotlib
matplotlib.use('agg')
import nose
from matplotlib.testing.noseclasses import KnownFailure
from matplotlib import default_test_modules as testmodules
from matplotlib import font_manager
import time
# Make sure the font caches are created before starting any possibly
# parallel tests
if font_manager._fmcache is not None:
while not os.path.exists(font_manager._fmcache):
time.sleep(0.5)
plugins = [KnownFailure]

# Nose doesn't automatically instantiate all of the plugins in the
# child processes, so we have to provide the multiprocess plugin
# with a list.
from nose.plugins import multiprocess
multiprocess._instantiate_plugins = plugins

if self.omit_pep8:
testmodules.remove('matplotlib.tests.test_coding_standards')
elif self.pep8_only:
testmodules = ['matplotlib.tests.test_coding_standards']

nose.main(addplugins=[x() for x in plugins],
defaultTest=testmodules,
argv=['nosetests'] + self.test_args,
exit=False)


# One doesn't normally see `if __name__ == '__main__'` blocks in a setup.py,
# however, this is needed on Windows to avoid creating infinite subprocesses
# when using multiprocessing.
Expand All @@ -135,6 +248,7 @@
package_dir = {'': 'lib'}
install_requires = []
setup_requires = []
tests_require = []
default_backend = None

# Go through all of the packages and figure out which ones we are
Expand Down Expand Up @@ -195,6 +309,7 @@
package_data[key] = list(set(val + package_data[key]))
install_requires.extend(package.get_install_requires())
setup_requires.extend(package.get_setup_requires())
tests_require.extend(package.get_tests_require())

# Write the default matplotlibrc file
if default_backend is None:
Expand Down Expand Up @@ -254,11 +369,13 @@
# List third-party Python packages that we require
install_requires=install_requires,
setup_requires=setup_requires,
tests_require=tests_require,

# matplotlib has C/C++ extensions, so it's not zip safe.
# Telling setuptools this prevents it from doing an automatic
# check for zip safety.
zip_safe=False,
cmdclass={'test': NoseTestCommand},

**extra_args
)
14 changes: 10 additions & 4 deletions setupext.py
Expand Up @@ -415,6 +415,12 @@ def get_setup_requires(self):
"""
return []

def get_tests_require(self):
"""
Get a list of Python packages that we require for executing tests.
"""
return []

def _check_for_pkg_config(self, package, include_file, min_version=None,
version=None):
"""
Expand Down Expand Up @@ -645,8 +651,8 @@ def check(self):

msgs = []
msg_template = ('{package} is required to run the matplotlib test '
'suite. pip/easy_install may attempt to install it '
'after matplotlib.')
'suite. "setup.py test" will automatically download it.'
' Install {package} to run matplotlib.test()')

bad_nose = msg_template.format(
package='nose %s or later' % self.nose_min_version
Expand Down Expand Up @@ -694,8 +700,8 @@ def get_package_data(self):
'sphinxext/tests/tinypages/_static/*',
]}

def get_install_requires(self):
requires = ['nose>=%s' % self.nose_min_version]
def get_tests_require(self):
requires = ['nose>=%s' % self.nose_min_version, 'sphinx']
if not sys.version_info >= (3, 3):
requires += ['mock']
return requires
Expand Down
1 change: 1 addition & 0 deletions tox.ini
Expand Up @@ -13,4 +13,5 @@ commands =
{envpython} {toxinidir}/tests.py --processes=-1 --process-timeout=300
deps =
nose
mock
numpy

0 comments on commit 6cd1ddd

Please sign in to comment.