diff --git a/.travis.yml b/.travis.yml index 98e12e3d76d2..a544ff11c67f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,7 +45,6 @@ env: - NPROC=2 - INSTALL_PEP8= - RUN_PEP8= - - NOSE_ARGS="-j $NPROC" - PYTEST_ARGS="-ra --maxfail=1 --timeout=300 --durations=25 --cov-report= --cov=lib -n $NPROC" - PYTHON_ARGS= - DELETE_FONT_CACHE= @@ -58,7 +57,7 @@ matrix: - python: 3.4 env: PYTHON_ARGS=-OO - python: 3.5 - env: PANDAS=pandas NOSE_ARGS=--with-coverage DELETE_FONT_CACHE=1 + env: PANDAS=pandas DELETE_FONT_CACHE=1 - python: 3.5 env: BUILD_DOCS=true - python: 3.5 @@ -70,7 +69,7 @@ matrix: - os: osx osx_image: xcode7.3 language: generic # https://github.com/travis-ci/travis-ci/issues/2312 - env: MOCK=mock NOSE_ARGS= + env: MOCK=mock cache: # As for now travis caches only "$HOME/.cache/pip" # https://docs.travis-ci.com/user/caching/#pip-cache @@ -117,9 +116,6 @@ install: pip install $PRE python-dateutil $NUMPY pyparsing!=2.1.6 $PANDAS cycler coveralls coverage $MOCK pip install $PRE -r doc-requirements.txt - # Install nose from a build which has partial - # support for python36 and suport for coverage output suppressing - pip install git+https://github.com/jenshnielsen/nose.git@matplotlibnose # pytest-cov>=2.3.1 due to https://github.com/pytest-dev/pytest-cov/issues/124 pip install $PRE pytest 'pytest-cov>=2.3.1' pytest-timeout pytest-xdist pytest-faulthandler $INSTALL_PEP8 @@ -155,21 +151,20 @@ script: if [[ $DELETE_FONT_CACHE == 1 ]]; then rm -rf ~/.cache/matplotlib fi + # Workaround for pytest-xdist flaky collection order + # https://github.com/pytest-dev/pytest/issues/920 + # https://github.com/pytest-dev/pytest/issues/1075 + export PYTHONHASHSEED=$(shuf -i 1-4294967295 -n 1) + echo PYTHONHASHSEED=$PYTHONHASHSEED + + echo The following args are passed to pytest $PYTEST_ARGS $RUN_PEP8 if [[ $USE_PYTEST == false ]]; then - echo The following args are passed to nose $NOSE_ARGS $RUN_PEP8 if [[ $TRAVIS_OS_NAME == 'osx' ]]; then - python tests.py $NOSE_ARGS $RUN_PEP8 + python tests.py $PYTEST_ARGS $RUN_PEP8 else - gdb -return-child-result -batch -ex r -ex bt --args python $PYTHON_ARGS tests.py $NOSE_ARGS $RUN_PEP8 + gdb -return-child-result -batch -ex r -ex bt --args python $PYTHON_ARGS tests.py $PYTEST_ARGS $RUN_PEP8 fi else - # Workaround for pytest-xdist flaky colletion order - # https://github.com/pytest-dev/pytest/issues/920 - # https://github.com/pytest-dev/pytest/issues/1075 - export PYTHONHASHSEED=$(shuf -i 1-4294967295 -n 1) - echo PYTHONHASHSEED=$PYTHONHASHSEED - - echo The following args are passed to pytest $PYTEST_ARGS $RUN_PEP8 py.test $PYTEST_ARGS $RUN_PEP8 fi else @@ -201,6 +196,11 @@ after_failure: fi after_success: + - | + if [[ $BUILD_DOCS == false ]]; then + coveralls + bash <(curl -s https://codecov.io/bash) + fi - | if [[ $TRAVIS_PULL_REQUEST == false && $TRAVIS_REPO_SLUG == 'matplotlib/matplotlib' && $BUILD_DOCS == true && $TRAVIS_BRANCH == 'master' ]]; then cd $TRAVIS_BUILD_DIR @@ -225,7 +225,3 @@ after_success: else echo "Will only deploy docs build from matplotlib master branch" fi - if [[ $NOSE_ARGS =~ "--with-coverage" || $USE_PYTEST == true ]]; then - coveralls - bash <(curl -s https://codecov.io/bash) - fi diff --git a/appveyor.yml b/appveyor.yml index fb6a0c76d139..6dfd857838df 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,11 +14,11 @@ environment: CMD_IN_ENV: "cmd /E:ON /V:ON /C obvci_appveyor_python_build_env.cmd" # Workaround for https://github.com/conda/conda-build/issues/636 PYTHONIOENCODING: "UTF-8" - PYTEST_ARGS: -ra --timeout=300 --durations=25 #--cov-report= --cov=lib #-n %NUMBER_OF_PROCESSORS% + PYTEST_ARGS: -ra --timeout=300 --durations=25 -n %NUMBER_OF_PROCESSORS% #--cov-report= --cov=lib USE_PYTEST: no - #PYTHONHASHSEED: 0 # Workaround for pytest-xdist flaky colletion order - # # https://github.com/pytest-dev/pytest/issues/920 - # # https://github.com/pytest-dev/pytest/issues/1075 + PYTHONHASHSEED: 0 # Workaround for pytest-xdist flaky collection order + # https://github.com/pytest-dev/pytest/issues/920 + # https://github.com/pytest-dev/pytest/issues/1075 matrix: # for testing purpose: numpy 1.8 on py2.7, for the rest use 1.10/latest @@ -91,12 +91,12 @@ install: - conda create -q -n test-environment python=%PYTHON_VERSION% pip setuptools numpy python-dateutil freetype=2.6 msinttypes "tk=8.5" pyparsing pytz tornado "libpng>=1.6.21,<1.7" "zlib=1.2" "cycler>=0.10" - nose mock sphinx + mock sphinx - activate test-environment - cmd: echo %PYTHON_VERSION% %TARGET_ARCH% - cmd: IF %PYTHON_VERSION% == 2.7 conda install -q functools32 # pytest-cov>=2.3.1 due to https://github.com/pytest-dev/pytest-cov/issues/124 - - if x%USE_PYTEST% == xyes conda install -q pytest "pytest-cov>=2.3.1" pytest-timeout #pytest-xdist + - conda install -q pytest "pytest-cov>=2.3.1" pytest-timeout pytest-xdist # Let the install prefer the static builds of the libs - set LIBRARY_LIB=%CONDA_PREFIX%\Library\lib @@ -116,7 +116,7 @@ install: test_script: # Now build the thing.. - - '%CMD_IN_ENV% python setup.py develop' + - '%CMD_IN_ENV% pip install --no-deps -ve .' # these should show no z, png, or freetype dll... - set "DUMPBIN=%VS140COMNTOOLS%\..\..\VC\bin\dumpbin.exe" #- cmd: '"%DUMPBIN%" /DEPENDENTS lib\matplotlib\_png*.pyd' @@ -135,9 +135,9 @@ test_script: # Test import of tkagg backend - python -c "import matplotlib as m; m.use('tkagg'); import matplotlib.pyplot as plt; print(plt.get_backend())" # tests - - if x%USE_PYTEST% == xyes echo The following args are passed to pytest %PYTEST_ARGS% + - echo The following args are passed to pytest %PYTEST_ARGS% - if x%USE_PYTEST% == xyes py.test %PYTEST_ARGS% - - if x%USE_PYTEST% == xno python tests.py + - if x%USE_PYTEST% == xno python tests.py %PYTEST_ARGS% # Generate a html for visual tests - python visual_tests.py diff --git a/conftest.py b/conftest.py deleted file mode 100644 index 18c720ba7002..000000000000 --- a/conftest.py +++ /dev/null @@ -1,76 +0,0 @@ -from __future__ import (absolute_import, division, print_function, - unicode_literals) - -import inspect -import os -import pytest -import unittest - -import matplotlib -matplotlib.use('agg') - -from matplotlib import default_test_modules - - -IGNORED_TESTS = { - 'matplotlib': [], -} - - -def blacklist_check(path): - """Check if test is blacklisted and should be ignored""" - head, tests_dir = os.path.split(path.dirname) - if tests_dir != 'tests': - return True - head, top_module = os.path.split(head) - return path.purebasename in IGNORED_TESTS.get(top_module, []) - - -def whitelist_check(path): - """Check if test is not whitelisted and should be ignored""" - left = path.dirname - last_left = None - module_path = path.purebasename - while len(left) and left != last_left: - last_left = left - left, tail = os.path.split(left) - module_path = '.'.join([tail, module_path]) - if module_path in default_test_modules: - return False - return True - - -COLLECT_FILTERS = { - 'none': lambda _: False, - 'blacklist': blacklist_check, - 'whitelist': whitelist_check, -} - - -def pytest_addoption(parser): - group = parser.getgroup("matplotlib", "matplotlib custom options") - - group.addoption('--collect-filter', action='store', - choices=COLLECT_FILTERS, default='blacklist', - help='filter tests during collection phase') - - group.addoption('--no-pep8', action='store_true', - help='skip PEP8 compliance tests') - - -def pytest_configure(config): - matplotlib._called_from_pytest = True - matplotlib._init_tests() - - if config.getoption('--no-pep8'): - IGNORED_TESTS['matplotlib'] += 'test_coding_standards' - - -def pytest_unconfigure(config): - matplotlib._called_from_pytest = False - - -def pytest_ignore_collect(path, config): - if path.ext == '.py': - collect_filter = config.getoption('--collect-filter') - return COLLECT_FILTERS[collect_filter](path) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 788d87e2ff92..86631ab05300 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1474,9 +1474,10 @@ def _jupyter_nbextension_paths(): default_test_modules = [ - 'matplotlib.tests.test_png', - 'matplotlib.tests.test_units', - ] + 'matplotlib.tests', + 'matplotlib.sphinxext.tests', + 'mpl_toolkits.tests', +] def _init_tests(): @@ -1510,19 +1511,51 @@ def _init_tests(): ) ) - from .testing._nose import check_deps - check_deps() + try: + import pytest + try: + from unittest import mock + except ImportError: + import mock + except ImportError: + print("matplotlib.test requires pytest and mock to run.") + raise -def test(verbosity=1, coverage=False, **kwargs): +def test(verbosity=None, coverage=False, switch_backend_warn=True, + recursionlimit=0, **kwargs): """run the matplotlib test suite""" _init_tests() - from .testing._nose import test as nose_test - return nose_test(verbosity, coverage, **kwargs) + old_backend = get_backend() + old_recursionlimit = sys.getrecursionlimit() + try: + use('agg') + if recursionlimit: + sys.setrecursionlimit(recursionlimit) + import pytest + + args = kwargs.pop('argv', []) + if not any(os.path.exists(arg) for arg in args): + args += ['--pyargs'] + default_test_modules + + if coverage: + args += ['--cov'] + + if verbosity: + args += ['-' + 'v' * verbosity] + + retcode = pytest.main(args, **kwargs) + finally: + if old_backend.lower() != 'agg': + use(old_backend, warn=switch_backend_warn) + if recursionlimit: + sys.setrecursionlimit(old_recursionlimit) + + return retcode -test.__test__ = False # nose: this function is not a test +test.__test__ = False # pytest: this function is not a test def _replacer(data, key): diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index b699f7d047ec..330cae8b79af 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -1555,7 +1555,7 @@ def gca(self, **kwargs): else: warnings.warn('Requested projection is different from ' 'current axis projection, creating new axis ' - 'with requested projection.') + 'with requested projection.', stacklevel=2) # no axes found, so create one which spans the figure return self.add_subplot(1, 1, 1, **kwargs) diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index 4bb0b0c5e97f..64b9406efc66 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -1284,13 +1284,16 @@ def findfont(self, prop, fontext='ttf', directory=None, cached = _lookup_cache[fontext].get(prop) if cached is not None: return cached + else: + directory = os.path.normcase(directory) best_score = 1e64 best_font = None for font in fontlist: if (directory is not None and - os.path.commonprefix([font.fname, directory]) != directory): + os.path.commonprefix([os.path.normcase(font.fname), + directory]) != directory): continue # Matching family should have highest priority, so it is multiplied # by 10.0 diff --git a/lib/matplotlib/gridspec.py b/lib/matplotlib/gridspec.py index 5476c5dfbe2d..a708b0fd0bb7 100644 --- a/lib/matplotlib/gridspec.py +++ b/lib/matplotlib/gridspec.py @@ -453,7 +453,6 @@ def get_position(self, fig, return_all=False): else: return figbox - def get_topmost_subplotspec(self): 'get the topmost SubplotSpec instance associated with the subplot' gridspec = self.get_gridspec() @@ -473,6 +472,10 @@ def __eq__(self, other): self.num1 == other.num1, self.num2 == other.num2)) + if six.PY2: + def __ne__(self, other): + return not self == other + def __hash__(self): return (hash(self._gridspec) ^ hash(self.num1) ^ diff --git a/lib/matplotlib/sphinxext/tests/conftest.py b/lib/matplotlib/sphinxext/tests/conftest.py index 0433c4979085..900743e86003 100644 --- a/lib/matplotlib/sphinxext/tests/conftest.py +++ b/lib/matplotlib/sphinxext/tests/conftest.py @@ -1,4 +1,5 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) -from matplotlib.tests.conftest import mpl_test_settings +from matplotlib.tests.conftest import (mpl_test_settings, + pytest_configure, pytest_unconfigure) diff --git a/lib/matplotlib/tests/conftest.py b/lib/matplotlib/tests/conftest.py index 5f2075a09485..918bafc873b9 100644 --- a/lib/matplotlib/tests/conftest.py +++ b/lib/matplotlib/tests/conftest.py @@ -6,6 +6,16 @@ import matplotlib +def pytest_configure(config): + matplotlib.use('agg') + matplotlib._called_from_pytest = True + matplotlib._init_tests() + + +def pytest_unconfigure(config): + matplotlib._called_from_pytest = False + + @pytest.fixture(autouse=True) def mpl_test_settings(request): from matplotlib.testing.decorators import _do_cleanup diff --git a/lib/matplotlib/tests/test_backend_pgf.py b/lib/matplotlib/tests/test_backend_pgf.py index edcab0d143ca..52151256f71a 100644 --- a/lib/matplotlib/tests/test_backend_pgf.py +++ b/lib/matplotlib/tests/test_backend_pgf.py @@ -99,9 +99,8 @@ def test_xelatex(): def test_pdflatex(): import os if os.environ.get('APPVEYOR', False): - from matplotlib.testing import xfail - xfail("pdflatex test does not work on appveyor due " - "to missing latex fonts") + pytest.xfail("pdflatex test does not work on appveyor due to missing " + "LaTeX fonts") rc_pdflatex = {'font.family': 'serif', 'pgf.rcfonts': False, diff --git a/lib/matplotlib/tests/test_category.py b/lib/matplotlib/tests/test_category.py index 6e5c43d76fb9..83847e3150bb 100644 --- a/lib/matplotlib/tests/test_category.py +++ b/lib/matplotlib/tests/test_category.py @@ -3,6 +3,8 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) +from distutils.version import LooseVersion + import pytest import numpy as np @@ -12,6 +14,11 @@ import unittest +needs_new_numpy = pytest.mark.xfail( + LooseVersion(np.__version__) < LooseVersion('1.8.0'), + reason='NumPy < 1.8.0 is broken.') + + class TestUnitData(object): testdata = [("hello world", ["hello world"], [0]), ("Здравствуйте мир", ["Здравствуйте мир"], [0]), @@ -21,12 +28,14 @@ class TestUnitData(object): ids = ["single", "unicode", "mixed"] + @needs_new_numpy @pytest.mark.parametrize("data, seq, locs", testdata, ids=ids) def test_unit(self, data, seq, locs): act = cat.UnitData(data) assert act.seq == seq assert act.locs == locs + @needs_new_numpy def test_update_map(self): data = ['a', 'd'] oseq = ['a', 'd'] @@ -78,6 +87,7 @@ class TestStrCategoryConverter(object): def mock_axis(self, request): self.cc = cat.StrCategoryConverter() + @needs_new_numpy @pytest.mark.parametrize("data, unitmap, exp", testdata, ids=ids) def test_convert(self, data, unitmap, exp): MUD = MockUnitData(unitmap) diff --git a/lib/mpl_toolkits/tests/conftest.py b/lib/mpl_toolkits/tests/conftest.py index 0433c4979085..900743e86003 100644 --- a/lib/mpl_toolkits/tests/conftest.py +++ b/lib/mpl_toolkits/tests/conftest.py @@ -1,4 +1,5 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) -from matplotlib.tests.conftest import mpl_test_settings +from matplotlib.tests.conftest import (mpl_test_settings, + pytest_configure, pytest_unconfigure) diff --git a/pytest.ini b/pytest.ini index 7a50474fe7e3..d9132bfa6002 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,5 +1,6 @@ [pytest] -norecursedirs = .git build ci dist extern release tools unit venv +norecursedirs = .git build ci dist doc extern lib/mpl_examples release tools unit venv +python_files = test_*.py pep8ignore = * E111 E114 E115 E116 E121 E122 E123 E124 E125 E126 E127 E128 E129 E131 E226 E240 E241 E242 E243 E244 E245 E246 E247 E248 E249 E265 E266 E704 W503 diff --git a/test_only.py b/setup_tests_only.py similarity index 100% rename from test_only.py rename to setup_tests_only.py diff --git a/tests.py b/tests.py index e2eeebc8d9e8..6a86ae84d4fd 100755 --- a/tests.py +++ b/tests.py @@ -4,10 +4,9 @@ # # $ python tests.py -v -d # -# The arguments are identical to the arguments accepted by nosetests. +# The arguments are identical to the arguments accepted by py.test. # -# See https://nose.readthedocs.org/ for a detailed description of -# these options. +# See http://doc.pytest.org/ for a detailed description of these options. import sys import argparse @@ -22,13 +21,13 @@ # The warnings need to be before any of matplotlib imports, but after # setuptools (if present) which has syntax error with the warnings enabled. - # Filtering by module does not work as this will be raised by Python itself. + # Filtering by module does not work as this will be raised by Python itself # so `module=matplotlib.*` is out of questions. import warnings - # Python 3.6 deprecate invalid character-pairs \A, \* ... in non raw-strings - # and other things. Let's not re-introduce them + # Python 3.6 deprecate invalid character-pairs \A, \* ... in non + # raw-strings and other things. Let's not re-introduce them warnings.filterwarnings('error', '.*invalid escape sequence.*', category=DeprecationWarning) warnings.filterwarnings( @@ -39,14 +38,8 @@ from matplotlib import test parser = argparse.ArgumentParser(add_help=False) - parser.add_argument('--no-pep8', action='store_true', - help='Run all tests except PEP8 testing') - parser.add_argument('--pep8', action='store_true', - help='Run only PEP8 testing') parser.add_argument('--no-network', action='store_true', help='Run tests without network connection') - parser.add_argument('-j', type=int, - help='Shortcut for specifying number of test processes') parser.add_argument('--recursionlimit', type=int, default=0, help='Specify recursionlimit for test run') args, extra_args = parser.parse_known_args() @@ -54,15 +47,10 @@ if args.no_network: from matplotlib.testing import disable_internet disable_internet.turn_off_internet() - extra_args.extend(['-a', '!network']) - if args.j: - extra_args.extend([ - '--processes={}'.format(args.j), - '--process-timeout=300' - ]) + extra_args.extend(['-m', 'not network']) - print('Python byte-compilation optimization level: %d' % sys.flags.optimize) + print('Python byte-compilation optimization level:', sys.flags.optimize) - success = test(argv=sys.argv[0:1] + extra_args, switch_backend_warn=False, + retcode = test(argv=extra_args, switch_backend_warn=False, recursionlimit=args.recursionlimit) - sys.exit(not success) + sys.exit(retcode)
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: