From 54ebda9948318747ba140e1934a87d98335063aa Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sun, 4 Nov 2018 19:10:11 +0100 Subject: [PATCH] make Axes._parse_scatter_color_args static --- lib/matplotlib/axes/_axes.py | 23 ++++++++++++++---- lib/matplotlib/tests/test_axes.py | 39 ++++++++++++++++++++----------- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index e5fd7047edb6..8d178bf9cef4 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -4013,7 +4013,9 @@ def dopatch(xs, ys, **kwargs): return dict(whiskers=whiskers, caps=caps, boxes=boxes, medians=medians, fliers=fliers, means=means) - def _parse_scatter_color_args(self, c, edgecolors, kwargs, xshape, yshape): + @staticmethod + def _parse_scatter_color_args(c, edgecolors, kwargs, xshape, yshape, + get_next_color_func): """ Helper function to process color related arguments of `.Axes.scatter`. @@ -4023,7 +4025,7 @@ def _parse_scatter_color_args(self, c, edgecolors, kwargs, xshape, yshape): - kwargs['facecolors'] - kwargs['facecolor'] - kwargs['color'] (==kwcolor) - - 'b' if in classic mode else next color from color cycle + - 'b' if in classic mode else the result of ``get_next_color_func()`` Argument precedence for edgecolors: @@ -4044,6 +4046,16 @@ def _parse_scatter_color_args(self, c, edgecolors, kwargs, xshape, yshape): Note: The dict is modified by this function. xshape, yshape : tuple of int The shape of the x and y arrays passed to `.Axes.scatter`. + get_next_color_func : callable + A callable that returns a color. This color is used as facecolor + if no other color is provided. + + Note, that this is a function rather than a fixed color value to + support conditional evaluation of the next color. As of the + current implementation obtaining the next color from the + property cycle advances the cycle. This must only happen if we + actually use the color, which will only be decided within this + method. Returns ------- @@ -4090,7 +4102,7 @@ def _parse_scatter_color_args(self, c, edgecolors, kwargs, xshape, yshape): if c is None: c = (facecolors if facecolors is not None else "b" if rcParams['_internal.classic_mode'] - else self._get_patches_for_fill.get_next_color()) + else get_next_color_func()) # After this block, c_array will be None unless # c is an array for mapping. The potential ambiguity @@ -4289,8 +4301,9 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, s = np.ma.ravel(s) # This doesn't have to match x, y in size. c, colors, edgecolors = \ - self._parse_scatter_color_args(c, edgecolors, kwargs, - xshape, yshape) + self._parse_scatter_color_args( + c, edgecolors, kwargs, xshape, yshape, + get_next_color_func=self._get_patches_for_fill.get_next_color) # `delete_masked_points` only modifies arguments of the same length as # `x`. diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 1b56c74ee3e7..7bf8fd1ecd36 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -1793,19 +1793,30 @@ def test_scatter_color(self): @pytest.mark.parametrize('c_case, re_key', params_test_scatter_c) def test_scatter_c(self, c_case, re_key): + def get_next_color(): + return 'blue' # currently unused + + from matplotlib.axes import Axes + + xshape = yshape = (4,) + # Additional checking of *c* (introduced in #11383). REGEXP = { "shape": "^'c' argument has [0-9]+ elements", # shape mismatch "conversion": "^'c' argument must be a mpl color", # bad vals } - x = y = [0, 1, 2, 3] - fig, ax = plt.subplots() if re_key is None: - ax.scatter(x, y, c=c_case, edgecolors="black") + Axes._parse_scatter_color_args( + c=c_case, edgecolors="black", kwargs={}, + xshape=xshape, yshape=yshape, + get_next_color_func=get_next_color) else: with pytest.raises(ValueError, match=REGEXP[re_key]): - ax.scatter(x, y, c=c_case, edgecolors="black") + Axes._parse_scatter_color_args( + c=c_case, edgecolors="black", kwargs={}, + xshape=xshape, yshape=yshape, + get_next_color_func=get_next_color) def _params(c=None, xshape=(2,), yshape=(2,), **kwargs): @@ -1829,11 +1840,12 @@ def _params(c=None, xshape=(2,), yshape=(2,), **kwargs): _result(c=['b', 'g'], colors=np.array([[0, 0, 1, 1], [0, .5, 0, 1]]))), ]) def test_parse_scatter_color_args(params, expected_result): + def get_next_color(): + return 'blue' # currently unused + from matplotlib.axes import Axes - dummyself = 'UNUSED' # self is only used in one case, which we do not - # test. Therefore we can get away without costly - # creating an Axes instance. - c, colors, _edgecolors = Axes._parse_scatter_color_args(dummyself, *params) + c, colors, _edgecolors = Axes._parse_scatter_color_args( + *params, get_next_color_func=get_next_color) assert c == expected_result.c assert_allclose(colors, expected_result.colors) @@ -1855,15 +1867,16 @@ def test_parse_scatter_color_args(params, expected_result): (dict(color='r', edgecolor='g'), 'g'), ]) def test_parse_scatter_color_args_edgecolors(kwargs, expected_edgecolors): + def get_next_color(): + return 'blue' # currently unused + from matplotlib.axes import Axes - dummyself = 'UNUSED' # self is only used in one case, which we do not - # test. Therefore we can get away without costly - # creating an Axes instance. c = kwargs.pop('c', None) edgecolors = kwargs.pop('edgecolors', None) _, _, result_edgecolors = \ - Axes._parse_scatter_color_args(dummyself, c, edgecolors, kwargs, - xshape=(2,), yshape=(2,)) + Axes._parse_scatter_color_args(c, edgecolors, kwargs, + xshape=(2,), yshape=(2,), + get_next_color_func=get_next_color) assert result_edgecolors == expected_edgecolors