Skip to content

Commit 6d7ff3c

Browse files
committed
TST: Skipif decorator for matplotlib #18190
1 parent 982ad07 commit 6d7ff3c

14 files changed

+111
-29
lines changed

pandas/tests/io/formats/test_style.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import pandas as pd
88
from pandas import DataFrame
99
import pandas.util.testing as tm
10+
from pandas.util._test_decorators import skip_if_no_mpl
1011

1112
jinja2 = pytest.importorskip('jinja2')
1213
from pandas.io.formats.style import Styler, _get_level_lengths # noqa
@@ -1011,8 +1012,8 @@ def test_hide_columns_mult_levels(self):
10111012

10121013
class TestStylerMatplotlibDep(object):
10131014

1015+
@skip_if_no_mpl
10141016
def test_background_gradient(self):
1015-
tm._skip_if_no_mpl()
10161017
df = pd.DataFrame([[1, 2], [2, 4]], columns=['A', 'B'])
10171018

10181019
for c_map in [None, 'YlOrRd']:

pandas/tests/plotting/common.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import pandas.util.testing as tm
1313
from pandas.util.testing import (ensure_clean,
1414
assert_is_valid_plot_return_object)
15+
from pandas.util._test_decorators import skip_if_no_mpl
1516

1617
import numpy as np
1718
from numpy import random
@@ -23,8 +24,6 @@
2324
This is a common base class used for various plotting tests
2425
"""
2526

26-
tm._skip_if_no_mpl()
27-
2827

2928
def _skip_if_no_scipy_gaussian_kde():
3029
try:
@@ -43,6 +42,7 @@ def _ok_for_gaussian_kde(kind):
4342
return plotting._compat._mpl_ge_1_5_0()
4443

4544

45+
@skip_if_no_mpl
4646
class TestPlotBase(object):
4747

4848
def setup_method(self, method):

pandas/tests/plotting/test_boxplot_method.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from pandas import Series, DataFrame, MultiIndex
99
from pandas.compat import range, lzip
1010
import pandas.util.testing as tm
11+
from pandas.util._test_decorators import skip_if_no_mpl
1112

1213
import numpy as np
1314
from numpy import random
@@ -19,8 +20,6 @@
1920

2021
""" Test cases for .boxplot method """
2122

22-
tm._skip_if_no_mpl()
23-
2423

2524
def _skip_if_mpl_14_or_dev_boxplot():
2625
# GH 8382
@@ -31,6 +30,7 @@ def _skip_if_mpl_14_or_dev_boxplot():
3130
pytest.skip("Matplotlib Regression in 1.4 and current dev.")
3231

3332

33+
@skip_if_no_mpl
3434
class TestDataFramePlots(TestPlotBase):
3535

3636
@pytest.mark.slow
@@ -174,6 +174,7 @@ def test_fontsize(self):
174174
xlabelsize=16, ylabelsize=16)
175175

176176

177+
@skip_if_no_mpl
177178
class TestDataFrameGroupByPlots(TestPlotBase):
178179

179180
@pytest.mark.slow

pandas/tests/plotting/test_datetimelike.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616

1717
from pandas.util.testing import assert_series_equal, ensure_clean
1818
import pandas.util.testing as tm
19+
from pandas.util._test_decorators import skip_if_no_mpl
1920

2021
from pandas.tests.plotting.common import (TestPlotBase,
2122
_skip_if_no_scipy_gaussian_kde)
2223

23-
tm._skip_if_no_mpl()
24-
2524

25+
@skip_if_no_mpl
2626
class TestTSPlot(TestPlotBase):
2727

2828
def setup_method(self, method):

pandas/tests/plotting/test_deprecated.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import pandas as pd
66
import pandas.util.testing as tm
7+
from pandas.util._test_decorators import skip_if_no_mpl
78
import pytest
89

910
from numpy.random import randn
@@ -18,9 +19,8 @@
1819
pandas.tools.plotting
1920
"""
2021

21-
tm._skip_if_no_mpl()
22-
2322

23+
@skip_if_no_mpl
2424
class TestDeprecatedNameSpace(TestPlotBase):
2525

2626
@pytest.mark.slow

pandas/tests/plotting/test_frame.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from pandas.compat import range, lrange, lmap, lzip, u, zip, PY3
1616
from pandas.io.formats.printing import pprint_thing
1717
import pandas.util.testing as tm
18+
from pandas.util._test_decorators import skip_if_no_mpl
1819

1920
import numpy as np
2021
from numpy.random import rand, randn
@@ -24,9 +25,8 @@
2425
_skip_if_no_scipy_gaussian_kde,
2526
_ok_for_gaussian_kde)
2627

27-
tm._skip_if_no_mpl()
28-
2928

29+
@skip_if_no_mpl
3030
class TestDataFramePlots(TestPlotBase):
3131

3232
def setup_method(self, method):

pandas/tests/plotting/test_groupby.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55

66
from pandas import Series, DataFrame
77
import pandas.util.testing as tm
8+
from pandas.util._test_decorators import skip_if_no_mpl
89

910
import numpy as np
1011

1112
from pandas.tests.plotting.common import TestPlotBase
1213

13-
tm._skip_if_no_mpl()
14-
1514

15+
@skip_if_no_mpl
1616
class TestDataFrameGroupByPlots(TestPlotBase):
1717

1818
def test_series_groupby_plotting_nominally_works(self):

pandas/tests/plotting/test_hist_method.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from pandas import Series, DataFrame
88
import pandas.util.testing as tm
9+
from pandas.util._test_decorators import skip_if_no_mpl
910

1011
import numpy as np
1112
from numpy.random import randn
@@ -14,9 +15,7 @@
1415
from pandas.tests.plotting.common import (TestPlotBase, _check_plot_works)
1516

1617

17-
tm._skip_if_no_mpl()
18-
19-
18+
@skip_if_no_mpl
2019
class TestSeriesPlots(TestPlotBase):
2120

2221
def setup_method(self, method):
@@ -141,6 +140,7 @@ def test_plot_fails_when_ax_differs_from_figure(self):
141140
self.ts.hist(ax=ax1, figure=fig2)
142141

143142

143+
@skip_if_no_mpl
144144
class TestDataFramePlots(TestPlotBase):
145145

146146
@pytest.mark.slow
@@ -251,6 +251,7 @@ def test_tight_layout(self):
251251
tm.close()
252252

253253

254+
@skip_if_no_mpl
254255
class TestDataFrameGroupByPlots(TestPlotBase):
255256

256257
@pytest.mark.slow

pandas/tests/plotting/test_misc.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from pandas import DataFrame
88
from pandas.compat import lmap
99
import pandas.util.testing as tm
10+
from pandas.util._test_decorators import skip_if_no_mpl
1011

1112
import numpy as np
1213
from numpy import random
@@ -15,9 +16,8 @@
1516
import pandas.plotting as plotting
1617
from pandas.tests.plotting.common import TestPlotBase, _check_plot_works
1718

18-
tm._skip_if_no_mpl()
19-
2019

20+
@skip_if_no_mpl
2121
class TestSeriesPlots(TestPlotBase):
2222

2323
def setup_method(self, method):
@@ -49,6 +49,7 @@ def test_bootstrap_plot(self):
4949
_check_plot_works(bootstrap_plot, series=self.ts, size=10)
5050

5151

52+
@skip_if_no_mpl
5253
class TestDataFramePlots(TestPlotBase):
5354

5455
def test_scatter_matrix_axis(self):

pandas/tests/plotting/test_series.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from pandas import Series, DataFrame, date_range
1313
from pandas.compat import range, lrange
1414
import pandas.util.testing as tm
15+
from pandas.util._test_decorators import skip_if_no_mpl
1516

1617
import numpy as np
1718
from numpy.random import randn
@@ -21,9 +22,8 @@
2122
_skip_if_no_scipy_gaussian_kde,
2223
_ok_for_gaussian_kde)
2324

24-
tm._skip_if_no_mpl()
25-
2625

26+
@skip_if_no_mpl
2727
class TestSeriesPlots(TestPlotBase):
2828

2929
def setup_method(self, method):

pandas/tests/test_resample.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import pandas as pd
1414
import pandas.tseries.offsets as offsets
1515
import pandas.util.testing as tm
16+
from pandas.util._test_decorators import skip_if_no_mpl
1617
from pandas import (Series, DataFrame, Panel, Index, isna,
1718
notna, Timestamp)
1819

@@ -234,9 +235,8 @@ def test_groupby_resample_on_api(self):
234235
result = df.groupby('key').resample('D', on='dates').mean()
235236
assert_frame_equal(result, expected)
236237

238+
@skip_if_no_mpl
237239
def test_plot_api(self):
238-
tm._skip_if_no_mpl()
239-
240240
# .resample(....).plot(...)
241241
# hitting warnings
242242
# GH 12448

pandas/tests/util/test_util.py

+18
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
validate_bool_kwarg)
1717

1818
import pandas.util.testing as tm
19+
from pandas.util._test_decorators import safe_import
1920

2021

2122
class TestDecorators(object):
@@ -482,3 +483,20 @@ def test_make_signature():
482483
assert sig == (['old_arg_name', 'new_arg_name',
483484
'mapping=None', 'stacklevel=2'],
484485
['old_arg_name', 'new_arg_name', 'mapping', 'stacklevel'])
486+
487+
488+
def test_safe_import(monkeypatch):
489+
assert not safe_import("foo")
490+
assert not safe_import("pandas", min_version="99.99.99")
491+
492+
# Create dummy module to be imported
493+
import types
494+
import sys
495+
mod_name = "hello123"
496+
mod = types.ModuleType(mod_name)
497+
mod.__version__ = "1.5"
498+
499+
assert not safe_import(mod_name)
500+
monkeypatch.setitem(sys.modules, mod_name, mod)
501+
assert not safe_import(mod_name, min_version="2.0")
502+
assert safe_import(mod_name, min_version="1.0")

pandas/util/_test_decorators.py

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
This module provides decorator functions which can be applied to test objects
3+
in order to skip those objects when certain conditions occur. A sample use case
4+
is to detect if the platform is missing ``matplotlib``. If so, any test objects
5+
which require ``matplotlib`` and marked with ``skip_if_no_mpl`` will be skipped
6+
by ``pytest`` during the execution of the test suite.
7+
8+
The decorators can be applied to classes:
9+
10+
@skip_if_some_reason
11+
class Foo():
12+
...
13+
14+
Or individual functions:
15+
16+
@skip_if_some_reason
17+
def test_foo():
18+
...
19+
20+
For more information, refer to the ``pytest`` documentation on ``skipif``.
21+
"""
22+
23+
import pytest
24+
25+
26+
def safe_import(mod_name, min_version=None):
27+
"""
28+
Parameters:
29+
-----------
30+
mod_name : str
31+
Name of the module to be imported
32+
min_version : str, default None
33+
Minimum required version of the specified mod_name
34+
35+
Returns:
36+
--------
37+
object
38+
The imported module if successful, or False
39+
"""
40+
try:
41+
mod = __import__(mod_name)
42+
except ImportError:
43+
return False
44+
45+
if not min_version:
46+
return mod
47+
else:
48+
import sys
49+
version = getattr(sys.modules[mod_name], '__version__')
50+
if version:
51+
from distutils.version import LooseVersion
52+
if LooseVersion(version) >= LooseVersion(min_version):
53+
return mod
54+
55+
return False
56+
57+
58+
def _skip_if_no_mpl():
59+
mod = safe_import("matplotlib")
60+
if mod:
61+
mod.use("Agg", warn=False)
62+
else:
63+
return True
64+
65+
66+
skip_if_no_mpl = pytest.mark.skipif(_skip_if_no_mpl(),
67+
reason="Missing matplotlib dependency")

pandas/util/testing.py

-7
Original file line numberDiff line numberDiff line change
@@ -325,13 +325,6 @@ def _skip_if_32bit():
325325
pytest.skip("skipping for 32 bit")
326326

327327

328-
def _skip_if_no_mpl():
329-
import pytest
330-
331-
mpl = pytest.importorskip("matplotlib")
332-
mpl.use("Agg", warn=False)
333-
334-
335328
def _skip_if_mpl_1_5():
336329
import matplotlib as mpl
337330

0 commit comments

Comments
 (0)