diff --git a/mplexporter/tests/test_basic.py b/mplexporter/tests/test_basic.py index b2e1eef..de81a74 100644 --- a/mplexporter/tests/test_basic.py +++ b/mplexporter/tests/test_basic.py @@ -16,9 +16,18 @@ def fake_renderer_output(fig, Renderer): return renderer.output +# Separate function such that pytest fail reports are more readable, +# because they print the values of the args, i.e. lines1, lines2. +def _assert_output_equal_clean(lines1, lines2): + for l1, l2 in zip(lines1, lines2): + assert l1 == l2 + + def _assert_output_equal(text1, text2): - for line1, line2 in zip(text1.strip().split(), text2.strip().split()): - assert line1 == line2 + _assert_output_equal_clean( + [l.strip() for l in text1.strip().split('\n')], + [l.strip() for l in text2.strip().split('\n')], + ) def test_lines(): diff --git a/mplexporter/tests/test_utils.py b/mplexporter/tests/test_utils.py index 51dad80..2a64a8d 100644 --- a/mplexporter/tests/test_utils.py +++ b/mplexporter/tests/test_utils.py @@ -1,4 +1,5 @@ from numpy.testing import assert_allclose, assert_equal +from matplotlib import ticker from . import plt from .. import utils @@ -33,3 +34,23 @@ def test_axis_w_fixed_formatter(): # NOTE: Issue #471 # assert_equal(props['tickformat'], labels) + +def test_axis_w_funcformatter_autolocator(): + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + ax.set_xlim(0, 1) + + def formatter(value, position): + return f"{position}:{value:.1f}" + + ax.xaxis.set_major_formatter(ticker.FuncFormatter(formatter)) + + props = utils.get_axis_properties(ax.xaxis) + + assert_equal(props['tickformat_formatter'], "fixed") + assert props['tickvalues'] is not None + expected = [formatter(value, i) + for i, value in enumerate(props['tickvalues'])] + assert_equal(props['tickformat'], expected) + + plt.close(fig) diff --git a/mplexporter/utils.py b/mplexporter/utils.py index f9467d7..abc8a6c 100644 --- a/mplexporter/utils.py +++ b/mplexporter/utils.py @@ -206,11 +206,9 @@ def get_axis_properties(axis): # Use tick values if appropriate locator = axis.get_major_locator() - props['nticks'] = len(locator()) - if isinstance(locator, ticker.FixedLocator): - props['tickvalues'] = list(locator()) - else: - props['tickvalues'] = None + tick_locs = list(locator()) # We'll use them later in some cases. + props['nticks'] = len(tick_locs) + props['tickvalues'] = tick_locs if isinstance(locator, ticker.FixedLocator) else None # Find tick formats props['tickformat_formatter'] = "" @@ -235,9 +233,12 @@ def get_axis_properties(axis): elif isinstance(formatter, ticker.FixedFormatter): props['tickformat'] = list(formatter.seq) props['tickformat_formatter'] = "fixed" - elif isinstance(formatter, ticker.FuncFormatter) and props['tickvalues']: - props['tickformat'] = [formatter(value) for value in props['tickvalues']] - props['tickformat_formatter'] = "func" + elif isinstance(formatter, ticker.FuncFormatter): + # It's impossible for JS to re-run our function, so run it now and save as Fixed. + if props['tickvalues'] is None: + props['tickvalues'] = tick_locs + props['tickformat'] = [formatter(value, i) for i, value in enumerate(props['tickvalues'])] + props['tickformat_formatter'] = "fixed" elif not any(label.get_visible() for label in axis.get_ticklabels()): props['tickformat'] = "" else: