Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove test dependencies from install_requires #4668

Merged
merged 8 commits into from Jul 14, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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=true
- python: "nightly"
Expand Down Expand Up @@ -103,9 +103,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