From ec56627a4b11b056555f8c9464f0a4d9f09c87fe Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Thu, 29 Mar 2018 13:43:13 -0500 Subject: [PATCH 1/5] ERR: Better error message for missing matplotlib Closes https://github.com/pandas-dev/pandas/issues/19810 --- doc/source/whatsnew/v0.23.0.txt | 1 + pandas/plotting/_core.py | 15 ++++++++++++++- pandas/tests/plotting/test_misc.py | 8 ++++++++ pandas/util/_test_decorators.py | 2 ++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index c6dadb7589869..5f347b5462ec4 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -958,6 +958,7 @@ Offsets Numeric ^^^^^^^ +- Better error message when matplotlib is not installed (:issue:`19810`). - Bug in :meth:`DataFrame.rank` and :meth:`Series.rank` when ``method='dense'`` and ``pct=True`` in which percentile ranks were not being used with the number of distinct observations (:issue:`15630`) - Bug in :class:`Series` constructor with an int or float list where specifying ``dtype=str``, ``dtype='str'`` or ``dtype='U'`` failed to convert the data elements to strings (:issue:`16605`) - Bug in :class:`Index` multiplication and division methods where operating with a ``Series`` would return an ``Index`` object instead of a ``Series`` object (:issue:`19042`) diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py index 6c3d07124215b..b272a434a624c 100644 --- a/pandas/plotting/_core.py +++ b/pandas/plotting/_core.py @@ -44,8 +44,9 @@ try: from pandas.plotting import _converter except ImportError: - pass + _HAS_MPL = False else: + _HAS_MPL = True if get_option('plotting.matplotlib.register_converters'): _converter.register(explicit=True) @@ -97,6 +98,9 @@ def __init__(self, data, kind=None, by=None, subplots=False, sharex=None, secondary_y=False, colormap=None, table=False, layout=None, **kwds): + if not _HAS_MPL: + raise ImportError("matplotlib is required for plotting.") + _converter._WARN = False self.data = data self.by = by @@ -2264,6 +2268,9 @@ def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None, ... }, index= ['pig', 'rabbit', 'duck', 'chicken', 'horse']) >>> hist = df.hist(bins=3) """ + if not _HAS_MPL: + raise ImportError("matplotlib is required for plotting.") + _converter._WARN = False if by is not None: axes = grouped_hist(data, column=column, by=by, ax=ax, grid=grid, @@ -2403,6 +2410,9 @@ def grouped_hist(data, column=None, by=None, ax=None, bins=50, figsize=None, ------- axes: collection of Matplotlib Axes """ + if not _HAS_MPL: + raise ImportError("matplotlib is required for plotting.") + _converter._WARN = False def plot_group(group, ax): @@ -2469,6 +2479,9 @@ def boxplot_frame_groupby(grouped, subplots=True, column=None, fontsize=None, >>> grouped = df.unstack(level='lvl1').groupby(level=0, axis=1) >>> boxplot_frame_groupby(grouped, subplots=False) """ + if not _HAS_MPL: + raise ImportError("matplotlib is required for plotting.") + _converter._WARN = False if subplots is True: naxes = len(grouped) diff --git a/pandas/tests/plotting/test_misc.py b/pandas/tests/plotting/test_misc.py index c5ce8aba9d80e..6b63a76def195 100644 --- a/pandas/tests/plotting/test_misc.py +++ b/pandas/tests/plotting/test_misc.py @@ -17,6 +17,14 @@ from pandas.tests.plotting.common import TestPlotBase, _check_plot_works +@td.skip_if_mpl +def test_import_error_message(): + df = DataFrame({"A": [1, 2]}) + + with tm.assert_raises_regex(ImportError, 'matplotlib is required'): + df.plot() + + @td.skip_if_no_mpl class TestSeriesPlots(TestPlotBase): diff --git a/pandas/util/_test_decorators.py b/pandas/util/_test_decorators.py index 8ad73538fbec1..b1edc86de8624 100644 --- a/pandas/util/_test_decorators.py +++ b/pandas/util/_test_decorators.py @@ -160,6 +160,8 @@ def decorated_func(func): skip_if_no_mpl = pytest.mark.skipif(_skip_if_no_mpl(), reason="Missing matplotlib dependency") +skip_if_mpl = pytest.mark.skipif(not _skip_if_no_mpl(), + reason="matplotlib is pressent") skip_if_mpl_1_5 = pytest.mark.skipif(_skip_if_mpl_1_5(), reason="matplotlib 1.5") xfail_if_mpl_2_2 = pytest.mark.xfail(_skip_if_mpl_2_2(), From 1159a750ab82d57fc1d3c11272c03cc679cf8080 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Thu, 29 Mar 2018 15:07:28 -0500 Subject: [PATCH 2/5] Better whatsnew --- doc/source/whatsnew/v0.23.0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index 5f347b5462ec4..bf94bedc456b1 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -958,7 +958,7 @@ Offsets Numeric ^^^^^^^ -- Better error message when matplotlib is not installed (:issue:`19810`). +- Better error message when attempting to plot but matplotlib is not installed (:issue:`19810`). - Bug in :meth:`DataFrame.rank` and :meth:`Series.rank` when ``method='dense'`` and ``pct=True`` in which percentile ranks were not being used with the number of distinct observations (:issue:`15630`) - Bug in :class:`Series` constructor with an int or float list where specifying ``dtype=str``, ``dtype='str'`` or ``dtype='U'`` failed to convert the data elements to strings (:issue:`16605`) - Bug in :class:`Index` multiplication and division methods where operating with a ``Series`` would return an ``Index`` object instead of a ``Series`` object (:issue:`19042`) From 300f37841f4fbf1712edec96dc65ada21aef54ec Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Fri, 30 Mar 2018 14:46:24 -0500 Subject: [PATCH 3/5] Move to the correct section [ci skip] [ci skip] --- doc/source/whatsnew/v0.23.0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index bf94bedc456b1..5f008a7bc8dea 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -958,7 +958,6 @@ Offsets Numeric ^^^^^^^ -- Better error message when attempting to plot but matplotlib is not installed (:issue:`19810`). - Bug in :meth:`DataFrame.rank` and :meth:`Series.rank` when ``method='dense'`` and ``pct=True`` in which percentile ranks were not being used with the number of distinct observations (:issue:`15630`) - Bug in :class:`Series` constructor with an int or float list where specifying ``dtype=str``, ``dtype='str'`` or ``dtype='U'`` failed to convert the data elements to strings (:issue:`16605`) - Bug in :class:`Index` multiplication and division methods where operating with a ``Series`` would return an ``Index`` object instead of a ``Series`` object (:issue:`19042`) @@ -1025,6 +1024,7 @@ I/O Plotting ^^^^^^^^ +- Better error message when attempting to plot but matplotlib is not installed (:issue:`19810`). - :func:`DataFrame.plot` now raises a ``ValueError`` when the ``x`` or ``y`` argument is improperly formed (:issue:`18671`) - Bug in :func:`DataFrame.plot` when ``x`` and ``y`` arguments given as positions caused incorrect referenced columns for line, bar and area plots (:issue:`20056`) - Bug in formatting tick labels with ``datetime.time()`` and fractional seconds (:issue:`18478`). From e9edde052bb427f512d83840480a0dc4a25a1fc9 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Mon, 2 Apr 2018 09:12:29 -0500 Subject: [PATCH 4/5] Fixups * comment * function --- pandas/plotting/_core.py | 22 ++++++++++------------ pandas/tests/plotting/test_misc.py | 1 + 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py index b272a434a624c..c5f72cb391572 100644 --- a/pandas/plotting/_core.py +++ b/pandas/plotting/_core.py @@ -51,6 +51,12 @@ _converter.register(explicit=True) +def _raise_if_no_mpl(): + # TODO(mpl_converter): remove once converter is explicit + if not _HAS_MPL: + raise ImportError("matplotlib is required for plotting.") + + def _get_standard_kind(kind): return {'density': 'kde'}.get(kind, kind) @@ -98,9 +104,7 @@ def __init__(self, data, kind=None, by=None, subplots=False, sharex=None, secondary_y=False, colormap=None, table=False, layout=None, **kwds): - if not _HAS_MPL: - raise ImportError("matplotlib is required for plotting.") - + _raise_if_no_mpl() _converter._WARN = False self.data = data self.by = by @@ -2268,9 +2272,7 @@ def hist_frame(data, column=None, by=None, grid=True, xlabelsize=None, ... }, index= ['pig', 'rabbit', 'duck', 'chicken', 'horse']) >>> hist = df.hist(bins=3) """ - if not _HAS_MPL: - raise ImportError("matplotlib is required for plotting.") - + _raise_if_no_mpl() _converter._WARN = False if by is not None: axes = grouped_hist(data, column=column, by=by, ax=ax, grid=grid, @@ -2410,9 +2412,7 @@ def grouped_hist(data, column=None, by=None, ax=None, bins=50, figsize=None, ------- axes: collection of Matplotlib Axes """ - if not _HAS_MPL: - raise ImportError("matplotlib is required for plotting.") - + _raise_if_no_mpl() _converter._WARN = False def plot_group(group, ax): @@ -2479,9 +2479,7 @@ def boxplot_frame_groupby(grouped, subplots=True, column=None, fontsize=None, >>> grouped = df.unstack(level='lvl1').groupby(level=0, axis=1) >>> boxplot_frame_groupby(grouped, subplots=False) """ - if not _HAS_MPL: - raise ImportError("matplotlib is required for plotting.") - + _raise_if_no_mpl() _converter._WARN = False if subplots is True: naxes = len(grouped) diff --git a/pandas/tests/plotting/test_misc.py b/pandas/tests/plotting/test_misc.py index 6b63a76def195..c82c939584dc7 100644 --- a/pandas/tests/plotting/test_misc.py +++ b/pandas/tests/plotting/test_misc.py @@ -19,6 +19,7 @@ @td.skip_if_mpl def test_import_error_message(): + # GH-19810 df = DataFrame({"A": [1, 2]}) with tm.assert_raises_regex(ImportError, 'matplotlib is required'): From b9f5bf65b352cf5a285852d6f062f5cf4bf62bb1 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Tue, 3 Apr 2018 08:42:27 +0200 Subject: [PATCH 5/5] typo --- pandas/util/_test_decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/util/_test_decorators.py b/pandas/util/_test_decorators.py index b1edc86de8624..ab6dfee9c862c 100644 --- a/pandas/util/_test_decorators.py +++ b/pandas/util/_test_decorators.py @@ -161,7 +161,7 @@ def decorated_func(func): skip_if_no_mpl = pytest.mark.skipif(_skip_if_no_mpl(), reason="Missing matplotlib dependency") skip_if_mpl = pytest.mark.skipif(not _skip_if_no_mpl(), - reason="matplotlib is pressent") + reason="matplotlib is present") skip_if_mpl_1_5 = pytest.mark.skipif(_skip_if_mpl_1_5(), reason="matplotlib 1.5") xfail_if_mpl_2_2 = pytest.mark.xfail(_skip_if_mpl_2_2(),