From 28bc96f6d8b42115af08b2bd275753cc362ab0ce Mon Sep 17 00:00:00 2001 From: Ryan May Date: Wed, 10 Oct 2018 00:23:49 -0600 Subject: [PATCH] Backport PR #12468: Fix `set_ylim` unit handling --- lib/matplotlib/axes/_base.py | 1 + lib/matplotlib/tests/test_units.py | 45 ++++++++++++++++++++++-------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 00457a29dfb8..05a3c824bcbf 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -3461,6 +3461,7 @@ def set_ylim(self, bottom=None, top=None, emit=True, auto=False, raise TypeError('Cannot pass both `ymax` and `top`') top = ymax + self._process_unit_info(ydata=(bottom, top)) bottom = self._validate_converted_limits(bottom, self.convert_yunits) top = self._validate_converted_limits(top, self.convert_yunits) diff --git a/lib/matplotlib/tests/test_units.py b/lib/matplotlib/tests/test_units.py index 20840d7b8702..fec498af3d4b 100644 --- a/lib/matplotlib/tests/test_units.py +++ b/lib/matplotlib/tests/test_units.py @@ -7,6 +7,7 @@ import numpy as np import datetime import platform +import pytest # Basic class that wraps numpy array and has units @@ -38,12 +39,8 @@ def __array__(self): return np.asarray(self.magnitude) -# Tests that the conversion machinery works properly for classes that -# work as a facade over numpy arrays (like pint) -@image_comparison(baseline_images=['plot_pint'], - tol={'aarch64': 0.02}.get(platform.machine(), 0.0), - extensions=['png'], remove_text=False, style='mpl20') -def test_numpy_facade(): +@pytest.fixture +def quantity_converter(): # Create an instance of the conversion interface and # mock so we can check methods called qc = munits.ConversionInterface() @@ -60,12 +57,29 @@ def convert(value, unit, axis): else: return Quantity(value, axis.get_units()).to(unit).magnitude + def default_units(value, axis): + if hasattr(value, 'units'): + return value.units + elif np.iterable(value): + for v in value: + if hasattr(v, 'units'): + return v.units + return None + qc.convert = MagicMock(side_effect=convert) qc.axisinfo = MagicMock(side_effect=lambda u, a: munits.AxisInfo(label=u)) - qc.default_units = MagicMock(side_effect=lambda x, a: x.units) + qc.default_units = MagicMock(side_effect=default_units) + return qc + +# Tests that the conversion machinery works properly for classes that +# work as a facade over numpy arrays (like pint) +@image_comparison(baseline_images=['plot_pint'], + tol={'aarch64': 0.02}.get(platform.machine(), 0.0), + extensions=['png'], remove_text=False, style='mpl20') +def test_numpy_facade(quantity_converter): # Register the class - munits.registry[Quantity] = qc + munits.registry[Quantity] = quantity_converter # Simple test y = Quantity(np.linspace(0, 30), 'miles') @@ -79,9 +93,9 @@ def convert(value, unit, axis): ax.yaxis.set_units('inches') ax.xaxis.set_units('seconds') - assert qc.convert.called - assert qc.axisinfo.called - assert qc.default_units.called + assert quantity_converter.convert.called + assert quantity_converter.axisinfo.called + assert quantity_converter.default_units.called # Tests gh-8908 @@ -97,6 +111,15 @@ def test_plot_masked_units(): ax.plot(data_masked_units) +def test_empty_set_limits_with_units(quantity_converter): + # Register the class + munits.registry[Quantity] = quantity_converter + + fig, ax = plt.subplots() + ax.set_xlim(Quantity(-1, 'meters'), Quantity(6, 'meters')) + ax.set_ylim(Quantity(-1, 'hours'), Quantity(16, 'hours')) + + @image_comparison(baseline_images=['jpl_bar_units'], extensions=['png'], savefig_kwarg={'dpi': 120}, style='mpl20') def test_jpl_bar_units():