From 367b68fbb7added330160e426830116cd247212a Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 26 Jul 2015 02:25:58 -0400 Subject: [PATCH 01/60] ENH: Make implicit x in plot pandas aware Try to grab `y.index` before returning `np.arange(len(y))` --- lib/matplotlib/axes/_base.py | 7 +++---- lib/matplotlib/cbook.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 77620aec1117..8305f010e215 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -31,7 +31,7 @@ import matplotlib.image as mimage from matplotlib.offsetbox import OffsetBox from matplotlib.artist import allow_rasterization -from matplotlib.cbook import iterable +from matplotlib.cbook import iterable, get_index_y from matplotlib.rcsetup import cycler rcParams = matplotlib.rcParams @@ -349,12 +349,11 @@ def _plot_args(self, tup, kwargs): if v is not None: kw[k] = v - y = np.atleast_1d(tup[-1]) - if len(tup) == 2: x = np.atleast_1d(tup[0]) + y = np.atleast_1d(tup[-1]) else: - x = np.arange(y.shape[0], dtype=float) + x, y = get_index_y(tup[-1]) x, y = self._xy_from_xy(x, y) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 00070bc5fac0..ed460315b1c1 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2487,6 +2487,34 @@ def pts_to_midstep(x, *args): 'step-post': pts_to_poststep, 'step-mid': pts_to_midstep} + +def get_index_y(y): + """ + A helper function to get the index of an input to plot + against if x values are not explicitly given. + + Tries to get `y.index` (works if this is a pd.Series), if that + fails, return np.arange(y.shape[0]). + + This will be extended in the future to deal with more types of + labeled data. + + Parameters + ---------- + y : scalar or array-like + The proposed y-value + + Returns + ------- + x, y : ndarray + The x and y values to plot. + """ + try: + return y.index.values, y.values + except AttributeError: + y = np.atleast_1d(y) + return np.arange(y.shape[0], dtype=float), y + # Numpy > 1.6.x deprecates putmask in favor of the new copyto. # So long as we support versions 1.6.x and less, we need the # following local version of putmask. We choose to make a From 34b879abb0b1c2976dac630d77289020e81494e5 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 25 Jul 2015 02:48:01 -0400 Subject: [PATCH 02/60] ENH: plotting methods can unpack labeled data After discussions with Brian Granger, Fernando Perez Peter Wang, Matthew Rocklin and Jake VanderPlas this is a proposal for how to deal with labeled data in matplotlib. The approach taken is that if the optional kwarg 'data' is passed in any other string args/kwargs to the function are replaced be the result `data[k]` if the key exists in `data`, else leave the value as-is. --- lib/matplotlib/__init__.py | 39 +++++++++++++++++++++++++++++- lib/matplotlib/axes/_axes.py | 46 +++++++++++++++++++++++++++++++++++- lib/matplotlib/rcsetup.py | 5 ++-- 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 5b7675e7cfe8..017140626e9a 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -115,7 +115,7 @@ import warnings import contextlib import distutils.sysconfig - +import functools # cbook must import matplotlib only within function # definitions, so it is safe to import from it here. from matplotlib.cbook import is_string_like, mplDeprecation @@ -1520,6 +1520,43 @@ def test(verbosity=1): test.__test__ = False # nose: this function is not a test + +def _replacer(data, key): + # if key isn't a string don't bother + if not isinstance(key, six.string_types): + return key + # try to use __getitem__ + try: + return data[key] + # key does not exist, silently fall back to key + except KeyError: + return key + + +def unpack_labeled_data(func): + """ + A decorator to add a 'data' kwarg to any a function. The signature + of the input function must be :: + + def foo(ax, *args, **kwargs) + + so this is suitable for use with Axes methods. + """ + @functools.wraps(func) + def inner(ax, *args, **kwargs): + data = kwargs.pop('data', None) + if data is not None: + if rcParams['unpack_labeled']: + args = tuple(_replacer(data, a) for a in args) + kwargs = dict((k, _replacer(data, v)) + for k, v in six.iteritems(kwargs)) + else: + raise ValueError("Trying to unpack labeled data, but " + "rcParams['unpack_labeled'] is False") + + return func(ax, *args, **kwargs) + return inner + verbose.report('matplotlib version %s' % __version__) verbose.report('verbose.level %s' % verbose.level) verbose.report('interactive is %s' % is_interactive()) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index bd5f08584c65..c271f674435c 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -11,6 +11,7 @@ from numpy import ma import matplotlib +from matplotlib import unpack_labeled_data import matplotlib.cbook as cbook from matplotlib.cbook import mplDeprecation, STEP_LOOKUP_MAP @@ -901,6 +902,7 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): self.autoscale_view(scaley=False) return p + @unpack_labeled_data @docstring.dedent def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', label='', **kwargs): @@ -979,6 +981,7 @@ def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', return coll + @unpack_labeled_data @docstring.dedent_interpd def vlines(self, x, ymin, ymax, colors='k', linestyles='solid', label='', **kwargs): @@ -1059,6 +1062,7 @@ def vlines(self, x, ymin, ymax, colors='k', linestyles='solid', return coll + @unpack_labeled_data @docstring.dedent_interpd def eventplot(self, positions, orientation='horizontal', lineoffsets=1, linelengths=1, linewidths=None, colors=None, @@ -1241,6 +1245,7 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, return colls #### Basic plotting + @unpack_labeled_data @docstring.dedent_interpd def plot(self, *args, **kwargs): """ @@ -1385,6 +1390,7 @@ def plot(self, *args, **kwargs): self.autoscale_view(scalex=scalex, scaley=scaley) return lines + @unpack_labeled_data @docstring.dedent_interpd def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False, **kwargs): @@ -1458,6 +1464,7 @@ def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False, return ret + @unpack_labeled_data @docstring.dedent_interpd def loglog(self, *args, **kwargs): """ @@ -1519,6 +1526,7 @@ def loglog(self, *args, **kwargs): return l + @unpack_labeled_data @docstring.dedent_interpd def semilogx(self, *args, **kwargs): """ @@ -1571,6 +1579,7 @@ def semilogx(self, *args, **kwargs): self._hold = b # restore the hold return l + @unpack_labeled_data @docstring.dedent_interpd def semilogy(self, *args, **kwargs): """ @@ -1623,6 +1632,7 @@ def semilogy(self, *args, **kwargs): return l + @unpack_labeled_data @docstring.dedent_interpd def acorr(self, x, **kwargs): """ @@ -1684,6 +1694,7 @@ def acorr(self, x, **kwargs): """ return self.xcorr(x, x, **kwargs) + @unpack_labeled_data @docstring.dedent_interpd def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none, usevlines=True, maxlags=10, **kwargs): @@ -1773,6 +1784,7 @@ def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none, #### Specialized plotting + @unpack_labeled_data def step(self, x, y, *args, **kwargs): """ Make a step plot. @@ -1810,6 +1822,7 @@ def step(self, x, y, *args, **kwargs): return self.plot(x, y, *args, **kwargs) + @unpack_labeled_data @docstring.dedent_interpd def bar(self, left, height, width=0.8, bottom=None, **kwargs): """ @@ -2236,6 +2249,7 @@ def barh(self, bottom, width, height=0.8, left=None, **kwargs): bottom=bottom, orientation='horizontal', **kwargs) return patches + @unpack_labeled_data @docstring.dedent_interpd def broken_barh(self, xranges, yrange, **kwargs): """ @@ -2288,6 +2302,7 @@ def broken_barh(self, xranges, yrange, **kwargs): return col + @unpack_labeled_data def stem(self, *args, **kwargs): """ Create a stem plot. @@ -2375,6 +2390,7 @@ def stem(self, *args, **kwargs): return stem_container + @unpack_labeled_data def pie(self, x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, radius=None, counterclock=True, @@ -2594,6 +2610,7 @@ def pie(self, x, explode=None, labels=None, colors=None, else: return slices, texts, autotexts + @unpack_labeled_data @docstring.dedent_interpd def errorbar(self, x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, capsize=None, @@ -2971,6 +2988,7 @@ def xywhere(xs, ys, mask): return errorbar_container # (l0, caplines, barcols) + @unpack_labeled_data def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, positions=None, widths=None, patch_artist=None, bootstrap=None, usermedians=None, conf_intervals=None, @@ -3256,6 +3274,7 @@ def _update_dict(dictionary, rc_name, properties): manage_xticks=manage_xticks) return artists + @unpack_labeled_data def bxp(self, bxpstats, positions=None, widths=None, vert=True, patch_artist=False, shownotches=False, showmeans=False, showcaps=True, showbox=True, showfliers=True, @@ -3640,6 +3659,7 @@ def dopatch(xs, ys, **kwargs): return dict(whiskers=whiskers, caps=caps, boxes=boxes, medians=medians, fliers=fliers, means=means) + @unpack_labeled_data @docstring.dedent_interpd def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, @@ -3847,6 +3867,7 @@ def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, return collection + @unpack_labeled_data @docstring.dedent_interpd def hexbin(self, x, y, C=None, gridsize=100, bins=None, xscale='linear', yscale='linear', extent=None, @@ -4347,6 +4368,7 @@ def quiverkey(self, *args, **kw): return qk quiverkey.__doc__ = mquiver.QuiverKey.quiverkey_doc + @unpack_labeled_data def quiver(self, *args, **kw): if not self._hold: self.cla() @@ -4357,10 +4379,12 @@ def quiver(self, *args, **kw): return q quiver.__doc__ = mquiver.Quiver.quiver_doc + @unpack_labeled_data def stackplot(self, x, *args, **kwargs): return mstack.stackplot(self, x, *args, **kwargs) stackplot.__doc__ = mstack.stackplot.__doc__ + @unpack_labeled_data def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None, cmap=None, norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, transform=None, zorder=1, start_points=None): @@ -4381,6 +4405,7 @@ def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None, return stream_container streamplot.__doc__ = mstream.streamplot.__doc__ + @unpack_labeled_data @docstring.dedent_interpd def barbs(self, *args, **kw): """ @@ -4397,6 +4422,7 @@ def barbs(self, *args, **kw): self.autoscale_view() return b + @unpack_labeled_data @docstring.dedent_interpd def fill(self, *args, **kwargs): """ @@ -4448,6 +4474,7 @@ def fill(self, *args, **kwargs): self.autoscale_view() return patches + @unpack_labeled_data @docstring.dedent_interpd def fill_between(self, x, y1, y2=0, where=None, interpolate=False, step=None, @@ -4601,6 +4628,7 @@ def get_interp_point(ind): self.autoscale_view() return collection + @unpack_labeled_data @docstring.dedent_interpd def fill_betweenx(self, y, x1, x2=0, where=None, step=None, **kwargs): @@ -4725,7 +4753,7 @@ def fill_betweenx(self, y, x1, x2=0, where=None, return collection #### plotting z(x,y): imshow, pcolor and relatives, contour - + @unpack_labeled_data @docstring.dedent_interpd def imshow(self, X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, vmin=None, vmax=None, @@ -4930,6 +4958,7 @@ def _pcolorargs(funcname, *args, **kw): C = C[:Ny - 1, :Nx - 1] return X, Y, C + @unpack_labeled_data @docstring.dedent_interpd def pcolor(self, *args, **kwargs): """ @@ -5206,6 +5235,7 @@ def pcolor(self, *args, **kwargs): self.add_collection(collection, autolim=False) return collection + @unpack_labeled_data @docstring.dedent_interpd def pcolormesh(self, *args, **kwargs): """ @@ -5354,6 +5384,7 @@ def pcolormesh(self, *args, **kwargs): self.add_collection(collection, autolim=False) return collection + @unpack_labeled_data @docstring.dedent_interpd def pcolorfast(self, *args, **kwargs): """ @@ -5541,6 +5572,7 @@ def pcolorfast(self, *args, **kwargs): self.autoscale_view(tight=True) return ret + @unpack_labeled_data def contour(self, *args, **kwargs): if not self._hold: self.cla() @@ -5548,6 +5580,7 @@ def contour(self, *args, **kwargs): return mcontour.QuadContourSet(self, *args, **kwargs) contour.__doc__ = mcontour.QuadContourSet.contour_doc + @unpack_labeled_data def contourf(self, *args, **kwargs): if not self._hold: self.cla() @@ -5588,6 +5621,7 @@ def table(self, **kwargs): #### Data analysis + @unpack_labeled_data @docstring.dedent_interpd def hist(self, x, bins=10, range=None, normed=False, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', @@ -6134,6 +6168,7 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, else: return n, bins, cbook.silent_list('Lists of Patches', patches) + @unpack_labeled_data @docstring.dedent_interpd def hist2d(self, x, y, bins=10, range=None, normed=False, weights=None, cmin=None, cmax=None, **kwargs): @@ -6227,6 +6262,7 @@ def hist2d(self, x, y, bins=10, range=None, normed=False, weights=None, return h, xedges, yedges, pc + @unpack_labeled_data @docstring.dedent_interpd def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, @@ -6351,6 +6387,7 @@ def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, else: return pxx, freqs, line + @unpack_labeled_data @docstring.dedent_interpd def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, @@ -6462,6 +6499,7 @@ def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, else: return pxy, freqs, line + @unpack_labeled_data @docstring.dedent_interpd def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, scale=None, @@ -6561,6 +6599,7 @@ def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] + @unpack_labeled_data @docstring.dedent_interpd def angle_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, **kwargs): @@ -6638,6 +6677,7 @@ def angle_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] + @unpack_labeled_data @docstring.dedent_interpd def phase_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, **kwargs): @@ -6715,6 +6755,7 @@ def phase_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] + @unpack_labeled_data @docstring.dedent_interpd def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, window=mlab.window_hanning, noverlap=0, pad_to=None, @@ -6782,6 +6823,7 @@ def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, return cxy, freqs + @unpack_labeled_data @docstring.dedent_interpd def specgram(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, @@ -7099,6 +7141,7 @@ def matshow(self, Z, **kwargs): integer=True)) return im + @unpack_labeled_data def violinplot(self, dataset, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False, points=100, bw_method=None): @@ -7203,6 +7246,7 @@ def _kde_method(X, coords): widths=widths, showmeans=showmeans, showextrema=showextrema, showmedians=showmedians) + @unpack_labeled_data def violin(self, vpstats, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False): """Drawing function for violin plots. diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index 2699311323ec..ad75ca7f50fd 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -1003,7 +1003,7 @@ def validate_cycler(s): 'xtick.minor.pad': [4, validate_float], # distance to label in points 'xtick.color': ['k', validate_color], # color of the xtick labels 'xtick.minor.visible': [False, validate_bool], # visiablility of the x axis minor ticks - + # fontsize of the xtick labels 'xtick.labelsize': ['medium', validate_fontsize], 'xtick.direction': ['in', six.text_type], # direction of xticks @@ -1161,7 +1161,8 @@ def validate_cycler(s): 'animation.convert_path': ['convert', six.text_type], # Additional arguments for mencoder movie writer (using pipes) - 'animation.convert_args': [[], validate_stringlist]} + 'animation.convert_args': [[], validate_stringlist], + 'unpack_labeled': [True, validate_bool], } if __name__ == '__main__': From 9f145cca019014cbcb76dd91fa7ec41ce450fa81 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 26 Jul 2015 04:45:51 -0400 Subject: [PATCH 03/60] ENH: add white-list of args/kwargs to relpace If white list is provided, only try to replace those. --- lib/matplotlib/__init__.py | 43 +++++++++++------ lib/matplotlib/axes/_axes.py | 89 ++++++++++++++++++------------------ 2 files changed, 73 insertions(+), 59 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 017140626e9a..eeacfac8db65 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1533,7 +1533,7 @@ def _replacer(data, key): return key -def unpack_labeled_data(func): +def unpack_labeled_data(wl_args=None, wl_kwargs=None): """ A decorator to add a 'data' kwarg to any a function. The signature of the input function must be :: @@ -1542,20 +1542,33 @@ def foo(ax, *args, **kwargs) so this is suitable for use with Axes methods. """ - @functools.wraps(func) - def inner(ax, *args, **kwargs): - data = kwargs.pop('data', None) - if data is not None: - if rcParams['unpack_labeled']: - args = tuple(_replacer(data, a) for a in args) - kwargs = dict((k, _replacer(data, v)) - for k, v in six.iteritems(kwargs)) - else: - raise ValueError("Trying to unpack labeled data, but " - "rcParams['unpack_labeled'] is False") - - return func(ax, *args, **kwargs) - return inner + if wl_kwargs is not None: + wl_kwargs = set(wl_kwargs) + if wl_args is not None: + wl_args = set(wl_args) + + def param(func): + @functools.wraps(func) + def inner(ax, *args, **kwargs): + data = kwargs.pop('data', None) + if data is not None: + if wl_args is None: + args = tuple(_replacer(data, a) for a in args) + else: + args = tuple(_replacer(data, a) if j in wl_args else a + for j, a in enumerate(args)) + + if wl_kwargs is None: + kwargs = dict((k, _replacer(data, v)) + for k, v in six.iteritems(kwargs)) + else: + kwargs = dict( + (k, _replacer(data, v) if k in wl_kwargs else v) + for k, v in six.iteritems(kwargs)) + + return func(ax, *args, **kwargs) + return inner + return param verbose.report('matplotlib version %s' % __version__) verbose.report('verbose.level %s' % verbose.level) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index c271f674435c..6c40445daef1 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -902,7 +902,7 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): self.autoscale_view(scaley=False) return p - @unpack_labeled_data + @unpack_labeled_data(wl_args={1, 2, 3}, wl_kwargs={'y', 'xmin', 'xmax'}) @docstring.dedent def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', label='', **kwargs): @@ -981,7 +981,7 @@ def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', return coll - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def vlines(self, x, ymin, ymax, colors='k', linestyles='solid', label='', **kwargs): @@ -1062,7 +1062,7 @@ def vlines(self, x, ymin, ymax, colors='k', linestyles='solid', return coll - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def eventplot(self, positions, orientation='horizontal', lineoffsets=1, linelengths=1, linewidths=None, colors=None, @@ -1245,7 +1245,7 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, return colls #### Basic plotting - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def plot(self, *args, **kwargs): """ @@ -1390,7 +1390,7 @@ def plot(self, *args, **kwargs): self.autoscale_view(scalex=scalex, scaley=scaley) return lines - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False, **kwargs): @@ -1464,7 +1464,7 @@ def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False, return ret - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def loglog(self, *args, **kwargs): """ @@ -1526,7 +1526,7 @@ def loglog(self, *args, **kwargs): return l - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def semilogx(self, *args, **kwargs): """ @@ -1579,7 +1579,7 @@ def semilogx(self, *args, **kwargs): self._hold = b # restore the hold return l - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def semilogy(self, *args, **kwargs): """ @@ -1632,7 +1632,7 @@ def semilogy(self, *args, **kwargs): return l - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def acorr(self, x, **kwargs): """ @@ -1694,7 +1694,7 @@ def acorr(self, x, **kwargs): """ return self.xcorr(x, x, **kwargs) - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none, usevlines=True, maxlags=10, **kwargs): @@ -1784,7 +1784,7 @@ def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none, #### Specialized plotting - @unpack_labeled_data + @unpack_labeled_data() def step(self, x, y, *args, **kwargs): """ Make a step plot. @@ -1822,7 +1822,7 @@ def step(self, x, y, *args, **kwargs): return self.plot(x, y, *args, **kwargs) - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def bar(self, left, height, width=0.8, bottom=None, **kwargs): """ @@ -2249,7 +2249,7 @@ def barh(self, bottom, width, height=0.8, left=None, **kwargs): bottom=bottom, orientation='horizontal', **kwargs) return patches - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def broken_barh(self, xranges, yrange, **kwargs): """ @@ -2302,7 +2302,7 @@ def broken_barh(self, xranges, yrange, **kwargs): return col - @unpack_labeled_data + @unpack_labeled_data() def stem(self, *args, **kwargs): """ Create a stem plot. @@ -2390,7 +2390,8 @@ def stem(self, *args, **kwargs): return stem_container - @unpack_labeled_data + @unpack_labeled_data(wl_args={1, 3, 4}, + wl_kwargs={'x', 'labels', 'colors'}) def pie(self, x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, radius=None, counterclock=True, @@ -2610,7 +2611,7 @@ def pie(self, x, explode=None, labels=None, colors=None, else: return slices, texts, autotexts - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def errorbar(self, x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, capsize=None, @@ -2988,7 +2989,7 @@ def xywhere(xs, ys, mask): return errorbar_container # (l0, caplines, barcols) - @unpack_labeled_data + @unpack_labeled_data() def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, positions=None, widths=None, patch_artist=None, bootstrap=None, usermedians=None, conf_intervals=None, @@ -3274,7 +3275,7 @@ def _update_dict(dictionary, rc_name, properties): manage_xticks=manage_xticks) return artists - @unpack_labeled_data + @unpack_labeled_data() def bxp(self, bxpstats, positions=None, widths=None, vert=True, patch_artist=False, shownotches=False, showmeans=False, showcaps=True, showbox=True, showfliers=True, @@ -3659,7 +3660,7 @@ def dopatch(xs, ys, **kwargs): return dict(whiskers=whiskers, caps=caps, boxes=boxes, medians=medians, fliers=fliers, means=means) - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, @@ -3867,7 +3868,7 @@ def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, return collection - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def hexbin(self, x, y, C=None, gridsize=100, bins=None, xscale='linear', yscale='linear', extent=None, @@ -4368,7 +4369,7 @@ def quiverkey(self, *args, **kw): return qk quiverkey.__doc__ = mquiver.QuiverKey.quiverkey_doc - @unpack_labeled_data + @unpack_labeled_data() def quiver(self, *args, **kw): if not self._hold: self.cla() @@ -4379,12 +4380,12 @@ def quiver(self, *args, **kw): return q quiver.__doc__ = mquiver.Quiver.quiver_doc - @unpack_labeled_data + @unpack_labeled_data() def stackplot(self, x, *args, **kwargs): return mstack.stackplot(self, x, *args, **kwargs) stackplot.__doc__ = mstack.stackplot.__doc__ - @unpack_labeled_data + @unpack_labeled_data() def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None, cmap=None, norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, transform=None, zorder=1, start_points=None): @@ -4405,7 +4406,7 @@ def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None, return stream_container streamplot.__doc__ = mstream.streamplot.__doc__ - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def barbs(self, *args, **kw): """ @@ -4422,7 +4423,7 @@ def barbs(self, *args, **kw): self.autoscale_view() return b - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def fill(self, *args, **kwargs): """ @@ -4474,7 +4475,7 @@ def fill(self, *args, **kwargs): self.autoscale_view() return patches - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def fill_between(self, x, y1, y2=0, where=None, interpolate=False, step=None, @@ -4628,7 +4629,7 @@ def get_interp_point(ind): self.autoscale_view() return collection - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def fill_betweenx(self, y, x1, x2=0, where=None, step=None, **kwargs): @@ -4753,7 +4754,7 @@ def fill_betweenx(self, y, x1, x2=0, where=None, return collection #### plotting z(x,y): imshow, pcolor and relatives, contour - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def imshow(self, X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, vmin=None, vmax=None, @@ -4958,7 +4959,7 @@ def _pcolorargs(funcname, *args, **kw): C = C[:Ny - 1, :Nx - 1] return X, Y, C - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def pcolor(self, *args, **kwargs): """ @@ -5235,7 +5236,7 @@ def pcolor(self, *args, **kwargs): self.add_collection(collection, autolim=False) return collection - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def pcolormesh(self, *args, **kwargs): """ @@ -5384,7 +5385,7 @@ def pcolormesh(self, *args, **kwargs): self.add_collection(collection, autolim=False) return collection - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def pcolorfast(self, *args, **kwargs): """ @@ -5572,7 +5573,7 @@ def pcolorfast(self, *args, **kwargs): self.autoscale_view(tight=True) return ret - @unpack_labeled_data + @unpack_labeled_data() def contour(self, *args, **kwargs): if not self._hold: self.cla() @@ -5580,7 +5581,7 @@ def contour(self, *args, **kwargs): return mcontour.QuadContourSet(self, *args, **kwargs) contour.__doc__ = mcontour.QuadContourSet.contour_doc - @unpack_labeled_data + @unpack_labeled_data() def contourf(self, *args, **kwargs): if not self._hold: self.cla() @@ -5621,7 +5622,7 @@ def table(self, **kwargs): #### Data analysis - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def hist(self, x, bins=10, range=None, normed=False, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', @@ -6168,7 +6169,7 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, else: return n, bins, cbook.silent_list('Lists of Patches', patches) - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def hist2d(self, x, y, bins=10, range=None, normed=False, weights=None, cmin=None, cmax=None, **kwargs): @@ -6262,7 +6263,7 @@ def hist2d(self, x, y, bins=10, range=None, normed=False, weights=None, return h, xedges, yedges, pc - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, @@ -6387,7 +6388,7 @@ def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, else: return pxx, freqs, line - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, @@ -6499,7 +6500,7 @@ def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, else: return pxy, freqs, line - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, scale=None, @@ -6599,7 +6600,7 @@ def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def angle_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, **kwargs): @@ -6677,7 +6678,7 @@ def angle_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def phase_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, **kwargs): @@ -6755,7 +6756,7 @@ def phase_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, window=mlab.window_hanning, noverlap=0, pad_to=None, @@ -6823,7 +6824,7 @@ def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, return cxy, freqs - @unpack_labeled_data + @unpack_labeled_data() @docstring.dedent_interpd def specgram(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, @@ -7141,7 +7142,7 @@ def matshow(self, Z, **kwargs): integer=True)) return im - @unpack_labeled_data + @unpack_labeled_data() def violinplot(self, dataset, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False, points=100, bw_method=None): @@ -7246,7 +7247,7 @@ def _kde_method(X, coords): widths=widths, showmeans=showmeans, showextrema=showextrema, showmedians=showmedians) - @unpack_labeled_data + @unpack_labeled_data() def violin(self, vpstats, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False): """Drawing function for violin plots. From a1c9c342a6091775d6c332feacdc4478323f8db9 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 26 Jul 2015 19:17:55 -0400 Subject: [PATCH 04/60] MNT: remove unused rcparam --- lib/matplotlib/rcsetup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index ad75ca7f50fd..ccae09cf7773 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -1161,8 +1161,7 @@ def validate_cycler(s): 'animation.convert_path': ['convert', six.text_type], # Additional arguments for mencoder movie writer (using pipes) - 'animation.convert_args': [[], validate_stringlist], - 'unpack_labeled': [True, validate_bool], } + 'animation.convert_args': [[], validate_stringlist]} if __name__ == '__main__': From 1e044691038c8a6777069870bf4ff659d0f5c12d Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 27 Jul 2015 12:36:25 -0400 Subject: [PATCH 05/60] MNT: python 2.6 does not support set literals --- lib/matplotlib/axes/_axes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 6c40445daef1..8dfb22b10242 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -902,7 +902,7 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): self.autoscale_view(scaley=False) return p - @unpack_labeled_data(wl_args={1, 2, 3}, wl_kwargs={'y', 'xmin', 'xmax'}) + @unpack_labeled_data(wl_args=[1, 2, 3], wl_kwargs=['y', 'xmin', 'xmax']) @docstring.dedent def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', label='', **kwargs): @@ -2390,8 +2390,8 @@ def stem(self, *args, **kwargs): return stem_container - @unpack_labeled_data(wl_args={1, 3, 4}, - wl_kwargs={'x', 'labels', 'colors'}) + @unpack_labeled_data(wl_args=[1, 3, 4], + wl_kwargs=['x', 'labels', 'colors']) def pie(self, x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, radius=None, counterclock=True, From d6e177f191af9cb31462f55714a625e7390a65d9 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 28 Jul 2015 02:13:23 -0400 Subject: [PATCH 06/60] ENH: pass at decorator which extracts a label Not tested or used. --- lib/matplotlib/__init__.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index eeacfac8db65..270b6dd83300 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1533,7 +1533,7 @@ def _replacer(data, key): return key -def unpack_labeled_data(wl_args=None, wl_kwargs=None): +def unpack_labeled_data(wl_args=None, wl_kwargs=None, label_pos=None): """ A decorator to add a 'data' kwarg to any a function. The signature of the input function must be :: @@ -1542,6 +1542,9 @@ def foo(ax, *args, **kwargs) so this is suitable for use with Axes methods. """ + if label_pos is not None: + label_arg, label_kwarg = label_pos + if wl_kwargs is not None: wl_kwargs = set(wl_kwargs) if wl_args is not None: @@ -1565,6 +1568,18 @@ def inner(ax, *args, **kwargs): kwargs = dict( (k, _replacer(data, v) if k in wl_kwargs else v) for k, v in six.iteritems(kwargs)) + if (label_pos is not None and ('label' not in kwargs or + kwargs['label'] is None)): + if len(args) > label_arg: + try: + kwargs['label'] = args[label_arg].name + except AttributeError: + pass + elif label_kwarg in kwargs: + try: + kwargs['label'] = args[label_kwarg].name + except AttributeError: + pass return func(ax, *args, **kwargs) return inner From 8a77f90dd3c00a2de42b82b38bb4bd081719a89a Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 29 Jul 2015 08:55:58 -0400 Subject: [PATCH 07/60] FIX: fix typo, slightly rename variables --- lib/matplotlib/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 270b6dd83300..68fa240302a6 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1556,20 +1556,20 @@ def inner(ax, *args, **kwargs): data = kwargs.pop('data', None) if data is not None: if wl_args is None: - args = tuple(_replacer(data, a) for a in args) + new_args = tuple(_replacer(data, a) for a in args) else: - args = tuple(_replacer(data, a) if j in wl_args else a - for j, a in enumerate(args)) + new_args = tuple(_replacer(data, a) if j in wl_args else a + for j, a in enumerate(args)) if wl_kwargs is None: - kwargs = dict((k, _replacer(data, v)) - for k, v in six.iteritems(kwargs)) + new_kwargs = dict((k, _replacer(data, v)) + for k, v in six.iteritems(kwargs)) else: - kwargs = dict( + new_kwargs = dict( (k, _replacer(data, v) if k in wl_kwargs else v) for k, v in six.iteritems(kwargs)) if (label_pos is not None and ('label' not in kwargs or - kwargs['label'] is None)): + kwargs['label'] is None)): if len(args) > label_arg: try: kwargs['label'] = args[label_arg].name @@ -1577,11 +1577,11 @@ def inner(ax, *args, **kwargs): pass elif label_kwarg in kwargs: try: - kwargs['label'] = args[label_kwarg].name + kwargs['label'] = kwargs[label_kwarg].name except AttributeError: pass - return func(ax, *args, **kwargs) + return func(ax, *new_args, **new_kwargs) return inner return param From dbd3445a85bdf13460a902971c158812f6fca6e2 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 29 Jul 2015 11:54:53 -0400 Subject: [PATCH 08/60] FIX: fix yet more typos --- lib/matplotlib/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 68fa240302a6..2e5dcacdc74e 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1568,6 +1568,9 @@ def inner(ax, *args, **kwargs): new_kwargs = dict( (k, _replacer(data, v) if k in wl_kwargs else v) for k, v in six.iteritems(kwargs)) + else: + new_args, new_kwargs = args, kwargs + if (label_pos is not None and ('label' not in kwargs or kwargs['label'] is None)): if len(args) > label_arg: @@ -1577,7 +1580,7 @@ def inner(ax, *args, **kwargs): pass elif label_kwarg in kwargs: try: - kwargs['label'] = kwargs[label_kwarg].name + new_kwargs['label'] = kwargs[label_kwarg].name except AttributeError: pass From 1ae230dcd485d08d6d6fae50628ac0a577aead16 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Thu, 30 Jul 2015 20:12:31 +0200 Subject: [PATCH 09/60] ENH: New version of unpack_labeled_data This version uses inspect to get the parameter names of a function and only needs the list of to-be-replaced argument names. In case inspect can't get a list of parameter names (e.g. *args is used), the developer has to supply a the complete list of argument names. This approach only needs for most cases only the list of replacements and only in the worst case (*args) a additional list of the names of all positional parameters and not synced two lists of position and names. It also makes some "import/ compile time" checks which should warn devs when the decorator is used with wrong / insufficient arguments. This also includes the "guess the label"-feature. --- lib/matplotlib/__init__.py | 140 ++++++++--- lib/matplotlib/axes/_axes.py | 5 +- .../tests/test_labled_data_unpacking.py | 224 ++++++++++++++++++ 3 files changed, 335 insertions(+), 34 deletions(-) create mode 100644 lib/matplotlib/tests/test_labled_data_unpacking.py diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 2e5dcacdc74e..aea13119573f 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -108,6 +108,7 @@ from itertools import chain import io +import inspect import locale import os import re @@ -1533,61 +1534,138 @@ def _replacer(data, key): return key -def unpack_labeled_data(wl_args=None, wl_kwargs=None, label_pos=None): +def unpack_labeled_data(replace_names=None, label_namer="y", positional_parameter_names=None): """ A decorator to add a 'data' kwarg to any a function. The signature - of the input function must be :: + of the input function must include the ax argument at the first position :: def foo(ax, *args, **kwargs) so this is suitable for use with Axes methods. - """ - if label_pos is not None: - label_arg, label_kwarg = label_pos - if wl_kwargs is not None: - wl_kwargs = set(wl_kwargs) - if wl_args is not None: - wl_args = set(wl_args) + Parameters + ---------- + replace_names : list of strings, optional, default: None + The list of parameter names which arguments should be replaced by `data[name]`. If None, + all arguments are replaced if they are included in `data`. + label_namer : string, optional, default: 'y' + The name of the parameter which argument should be used as label, if label is not set. If + None, the label keyword argument is not set. + positional_parameter_names : list of strings, optional, default: None + The full list of positional parameter names (including the `ax` argument at the first place + and including all possible positional parameter in `*args`), in the right order. Can also + include all other keyword parameter. Only needed if the wrapped function does contain + `*args` and replace_names is not None. + """ + if replace_names is not None: + replace_names = set(replace_names) def param(func): + # this call is deprecated in 3.x and will be removed in py3.6 :-( + # TODO: implement the same for py 3.x/3.6+ with inspect.signature(..) + arg_spec = inspect.getargspec(func) + _arg_names = arg_spec.args + _has_no_varargs = arg_spec.varargs is None + _has_varkwargs = not arg_spec.keywords is None + + # there can't be any positional arguments behind *args and no positional args can end up + # in **kwargs, so we only need to check for varargs: + # http://stupidpythonideas.blogspot.de/2013/08/arguments-and-parameters.html + if _has_no_varargs: + # remove the first "ax" arg + arg_names = _arg_names[1:] + else: + # in this case we need a supplied list of arguments or we need to replace all variables + # -> compile time check + if replace_names is None: + # all argnames should be replaced + arg_names = None + elif len(replace_names) == 0: + # No argnames should be replaced + arg_names = [] + else: + assert not (positional_parameter_names is None), "Got replace_names and wrapped function uses *args, need positional_parameter_names!" + # remove ax arg + arg_names = positional_parameter_names[1:] + + # compute the possible label_namer and label position in positional arguments + label_pos = 9999 # bigger than all "possible" argument lists + label_namer_pos = 9999 # bigger than all "possible" argument lists + if label_namer and arg_names and (label_namer in arg_names): + label_namer_pos = arg_names.index(label_namer) + if "label" in arg_names: + label_pos = arg_names.index("label") + + # Check the case we know a label_namer but we can't find it the arg_names... + # Unfortunately the label_namer can be in **kwargs, which we can't detect here and which + # results in a non-set label which might surprise the user :-( + if label_namer and not _has_varkwargs: + if not arg_names: + msg = "label_namer '%s' can't be found as the parameter without 'positional_parameter_names'." + raise AssertionError(msg % (label_namer)) + elif label_namer not in arg_names: + msg = "label_namer '%s' can't be found in the parameter names (known argnames: %s)." + raise AssertionError(msg % (label_namer, arg_names)) + else: + # this is the case when the name is in arg_names + pass + @functools.wraps(func) def inner(ax, *args, **kwargs): data = kwargs.pop('data', None) + label = None if data is not None: - if wl_args is None: - new_args = tuple(_replacer(data, a) for a in args) + # save the current label_namer value so that it can be used as a label + if label_namer_pos < len(args): + label = args[label_namer_pos] else: - new_args = tuple(_replacer(data, a) if j in wl_args else a - for j, a in enumerate(args)) - - if wl_kwargs is None: - new_kwargs = dict((k, _replacer(data, v)) - for k, v in six.iteritems(kwargs)) + label = kwargs.get(label_namer, None) + # ensure a string, as label can't be anything else + if not isinstance(label, six.string_types): + label = None + + if replace_names is None: + # all should be replaced + args = tuple(_replacer(data, a) for j, a in enumerate(args)) + kwargs = dict((k, _replacer(data, v)) for k, v in six.iteritems(kwargs)) else: - new_kwargs = dict( - (k, _replacer(data, v) if k in wl_kwargs else v) + # An arg is replaced if the arg_name of that position is in replace_names ... + if len(arg_names) < len(args): + raise RuntimeError("Got more args than function expects") + args = tuple(_replacer(data, a) if arg_names[j] in replace_names else a + for j, a in enumerate(args)) + # ... or a kwarg of that name in replace_names + kwargs = dict((k, _replacer(data, v) if k in replace_names else v) for k, v in six.iteritems(kwargs)) - else: - new_args, new_kwargs = args, kwargs - if (label_pos is not None and ('label' not in kwargs or - kwargs['label'] is None)): - if len(args) > label_arg: + # replace the label if this func "wants" a label arg and the user didn't set one + # Note: if the usere puts in "label=None", it does *NOT* get replaced! + user_supplied_label = ( + (len(args) >= label_pos) or # label is included in args + ('label' in kwargs) # ... or in kwargs + ) + if (label_namer and not user_supplied_label): + if label_namer_pos < len(args): try: - kwargs['label'] = args[label_arg].name + kwargs['label'] = args[label_namer_pos].name except AttributeError: - pass - elif label_kwarg in kwargs: + kwargs['label'] = label + elif label_namer in kwargs: try: - new_kwargs['label'] = kwargs[label_kwarg].name + kwargs['label'] = kwargs[label_namer].name except AttributeError: - pass - - return func(ax, *new_args, **new_kwargs) + kwargs['label'] = label + else: + import warnings + msg = "Tried to set a label via parameter '%s' but couldn't find such an argument. \n"\ + "(This is a programming error, please report to the matplotlib list!)" + warnings.warn(msg, RuntimeWarning, stacklevel=2) + #raise Exception() + return func(ax, *args, **kwargs) return inner return param + verbose.report('matplotlib version %s' % __version__) verbose.report('verbose.level %s' % verbose.level) verbose.report('interactive is %s' % is_interactive()) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 8dfb22b10242..c89f283e1f1c 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -902,7 +902,7 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): self.autoscale_view(scaley=False) return p - @unpack_labeled_data(wl_args=[1, 2, 3], wl_kwargs=['y', 'xmin', 'xmax']) + @unpack_labeled_data(replace_names=['y', 'xmin', 'xmax']) @docstring.dedent def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', label='', **kwargs): @@ -2390,8 +2390,7 @@ def stem(self, *args, **kwargs): return stem_container - @unpack_labeled_data(wl_args=[1, 3, 4], - wl_kwargs=['x', 'labels', 'colors']) + @unpack_labeled_data(replace_names=['x', 'labels', 'colors'], label_namer="x") def pie(self, x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, radius=None, counterclock=True, diff --git a/lib/matplotlib/tests/test_labled_data_unpacking.py b/lib/matplotlib/tests/test_labled_data_unpacking.py new file mode 100644 index 000000000000..27bab85c4b91 --- /dev/null +++ b/lib/matplotlib/tests/test_labled_data_unpacking.py @@ -0,0 +1,224 @@ +from __future__ import (absolute_import, division, print_function, + unicode_literals) + +from nose.tools import assert_raises, assert_equal +from nose.plugins.skip import SkipTest + +from .. import unpack_labeled_data + +# these two get used in multiple tests, so define them here +@unpack_labeled_data(replace_names=["x","y"]) +def plot_func(ax, x, y, ls="x", label=None, w="xyz"): + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) + + +@unpack_labeled_data(replace_names=["x","y"], positional_parameter_names=["ax", "x", "y", "ls", "label", "w"]) +def plot_func_varags(ax, *args, **kwargs): + all_args = [None, None, "x", None, "xyz"] + for i, v in enumerate(args): + all_args[i] = v + for i, k in enumerate(["x", "y", "ls", "label", "w"]): + if k in kwargs: + all_args[i] = kwargs[k] + x, y, ls, label, w = all_args + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) + + +all_funcs = [plot_func, plot_func_varags] + +d# these two get used in multiple tests, so define them here +@unpack_labeled_data(replace_names=["x","y"]) +def plot_func(ax, x, y, ls="x", label=None, w="xyz"): + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) + + +@unpack_labeled_data(replace_names=["x","y"], positional_parameter_names=["ax", "x", "y", "ls", "label", "w"]) +def plot_func_varags(ax, *args, **kwargs): + all_args = [None, None, "x", None, "xyz"] + for i, v in enumerate(args): + all_args[i] = v + for i, k in enumerate(["x", "y", "ls", "label", "w"]): + if k in kwargs: + all_args[i] = kwargs[k] + x, y, ls, label, w = all_args + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) + + +all_funcs = [plot_func, plot_func_varags] + +def test_compiletime_checks(): + """test decorator invocations -> no replacements""" + def func(ax, x,y): pass + def func_args(ax, x,y,*args): pass + def func_kwargs(ax, x,y,**kwargs): pass + def func_no_ax_args(*args, **kwargs): pass + + # this is ok + unpack_labeled_data(replace_names=["x","y"])(func) + unpack_labeled_data(replace_names=["x","y"])(func_kwargs) + + # no positional_parameter_names but needed due to replaces + def f(): + unpack_labeled_data(replace_names=["x","y"])(func_args) + assert_raises(AssertionError, f) + def f(): + unpack_labeled_data(replace_names=["x","y"])(func_no_ax_args) + assert_raises(AssertionError, f) + + # no replacements at all -> all ok... + unpack_labeled_data(replace_names=[], label_namer=None)(func) + unpack_labeled_data(replace_names=[], label_namer=None)(func_args) + unpack_labeled_data(replace_names=[], label_namer=None)(func_kwargs) + unpack_labeled_data(replace_names=[], label_namer=None)(func_no_ax_args) + + # label namer is unknown + def f(): + unpack_labeled_data(label_namer="z")(func) + assert_raises(AssertionError, f) + def f(): + unpack_labeled_data(label_namer="z")(func_args) + assert_raises(AssertionError, f) + # but "ok-ish", if func has kwargs -> will show up at runtime :-( + unpack_labeled_data(label_namer="z")(func_kwargs) + unpack_labeled_data(label_namer="z")(func_no_ax_args) + + +def test_label_problems_at_runtime(): + """These are tests for behaviour which would actually be nice to get rid of.""" + try: + from pandas.util.testing import assert_produces_warning + except ImportError: + raise SkipTest("Pandas not installed") + + @unpack_labeled_data(label_namer="z") + def func(*args, **kwargs):pass + + def f(): + func(None, x="a", y="b") + # This is a programming mistake: the parameter which should add the + # label is not present in the function call. Unfortunately this was masked + # due to the **kwargs useage + # This would be nice to handle as a compiletime check (see above...) + with assert_produces_warning(RuntimeWarning): + f() + + def real_func(x,y):pass + @unpack_labeled_data(label_namer="x") + def func(*args, **kwargs): + real_func(**kwargs) + + def f(): + func(None, x="a", y="b") + # This sets a label although the function can't handle it. + assert_raises(TypeError, f) + +def test_function_call_without_data(): + """test without data -> no replacements""" + for func in all_funcs: + assert_equal(func(None, "x","y"), "x: ['x'], y: ['y'], ls: x, w: xyz, label: None") + assert_equal(func(None, x="x",y="y") , "x: ['x'], y: ['y'], ls: x, w: xyz, label: None") + assert_equal(func(None, "x","y", label="") , "x: ['x'], y: ['y'], ls: x, w: xyz, label: ") + assert_equal(func(None, "x","y", label="text") , "x: ['x'], y: ['y'], ls: x, w: xyz, label: text") + assert_equal(func(None, x="x",y="y", label="") , "x: ['x'], y: ['y'], ls: x, w: xyz, label: ") + assert_equal(func(None, x="x",y="y", label="text") , "x: ['x'], y: ['y'], ls: x, w: xyz, label: text") + + +def test_function_call_with_dict_data(): + """Test with dict data -> label comes from the value of 'x' parameter """ + data = {"a":[1,2],"b":[8,9], "w":"NOT"} + for func in all_funcs: + assert_equal(func(None, "a","b", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func(None, x="a",y="b", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func(None, "a","b", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func(None, "a","b", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal(func(None, x="a",y="b", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func(None, x="a",y="b", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + + +def test_function_call_with_dict_data_not_in_data(): + "test for the case that one var is not in data -> half replaces, half kept" + data = {"a":[1,2], "w":"NOT"} + for func in all_funcs: + assert_equal(func(None, "a","b", data=data), "x: [1, 2], y: ['b'], ls: x, w: xyz, label: b") + assert_equal(func(None, x="a",y="b", data=data) , "x: [1, 2], y: ['b'], ls: x, w: xyz, label: b") + assert_equal(func(None, "a","b", label="", data=data) , "x: [1, 2], y: ['b'], ls: x, w: xyz, label: ") + assert_equal(func(None, "a","b", label="text", data=data) , "x: [1, 2], y: ['b'], ls: x, w: xyz, label: text") + assert_equal(func(None, x="a",y="b", label="", data=data) , "x: [1, 2], y: ['b'], ls: x, w: xyz, label: ") + assert_equal(func(None, x="a",y="b", label="text", data=data) , "x: [1, 2], y: ['b'], ls: x, w: xyz, label: text") + + +def test_function_call_with_pandas_data(): + """test with pandas dataframe -> label comes from data["col"].name """ + try: + import pandas as pd + except ImportError: + raise SkipTest("Pandas not installed") + + data = pd.DataFrame({"a":[1,2],"b":[8,9],"w":["NOT","NOT"]}) + + for func in all_funcs: + assert_equal(func(None, "a","b", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func(None, x="a",y="b", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func(None, "a","b", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func(None, "a","b", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal(func(None, x="a",y="b", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func(None, x="a",y="b", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + + +def test_function_call_replace_all(): + """Test without a "replace_names" argument, all vars should be replaced""" + data = {"a":[1,2],"b":[8,9], "x":"xyz"} + + @unpack_labeled_data() + def func_replace_all(ax, x, y, ls="x", label=None, w="NOT"): + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) + + assert_equal(func_replace_all(None, "a","b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func_replace_all(None, x="a",y="b", w="x", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func_replace_all(None, "a","b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func_replace_all(None, "a","b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal(func_replace_all(None, x="a",y="b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func_replace_all(None, x="a",y="b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + + @unpack_labeled_data() + def func_varags_replace_all(ax, *args, **kwargs): + all_args = [None, None, "x", None, "xyz"] + for i, v in enumerate(args): + all_args[i] = v + for i, k in enumerate(["x", "y", "ls", "label", "w"]): + if k in kwargs: + all_args[i] = kwargs[k] + x, y, ls, label, w = all_args + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) + + # in the first case, we can't get a "y" argument, as we don't know the names of the *args + assert_equal(func_varags_replace_all(None, "a","b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") + assert_equal(func_varags_replace_all(None, x="a",y="b", w="x", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func_varags_replace_all(None, "a","b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func_varags_replace_all(None, "a","b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal(func_varags_replace_all(None, x="a",y="b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func_varags_replace_all(None, x="a",y="b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + + +def test_no_label_replacements(): + """Test without "label_namer=None" -> no label replacement at all""" + + @unpack_labeled_data(replace_names=["x","y"], label_namer=None) + def func_no_label(ax, x, y, ls="x", label=None, w="xyz"): + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) + + data = {"a":[1,2],"b":[8,9], "w":"NOT"} + assert_equal(func_no_label(None, "a","b", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") + assert_equal(func_no_label(None, x="a",y="b", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") + assert_equal(func_no_label(None, "a","b", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func_no_label(None, "a","b", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + +def test_more_args_than_pos_parameter(): + @unpack_labeled_data(replace_names=["x","y"]) + def func(ax, x, y, z=1): + pass + + data = {"a":[1,2],"b":[8,9], "w":"NOT"} + def f(): + func(None, "a","b", "z", "z", data=data) + assert_raises(RuntimeError, f) From ab6d39c457e985db5f0a17f6b67f07f9e70b2be2 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Thu, 30 Jul 2015 22:13:59 +0200 Subject: [PATCH 10/60] FIX: Update .gitignore for pycharm project files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 341741a83fdd..16e1f0b996aa 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ .project .pydevproject .swp +.idea # Compiled source # ################### From 6e29d39d33f9aaa41b027f836a6fc0511535eca8 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Sun, 2 Aug 2015 22:50:50 +0200 Subject: [PATCH 11/60] ENH: proper arguments for labeled data decorator, part 1 --- lib/matplotlib/axes/_axes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index c89f283e1f1c..d6a3ee7f0980 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -2988,7 +2988,7 @@ def xywhere(xs, ys, mask): return errorbar_container # (l0, caplines, barcols) - @unpack_labeled_data() + @unpack_labeled_data(label_namer=None) def boxplot(self, x, notch=None, sym=None, vert=None, whis=None, positions=None, widths=None, patch_artist=None, bootstrap=None, usermedians=None, conf_intervals=None, @@ -3274,7 +3274,6 @@ def _update_dict(dictionary, rc_name, properties): manage_xticks=manage_xticks) return artists - @unpack_labeled_data() def bxp(self, bxpstats, positions=None, widths=None, vert=True, patch_artist=False, shownotches=False, showmeans=False, showcaps=True, showbox=True, showfliers=True, @@ -3659,7 +3658,8 @@ def dopatch(xs, ys, **kwargs): return dict(whiskers=whiskers, caps=caps, boxes=boxes, medians=medians, fliers=fliers, means=means) - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x", "y", "s", "c", "linewidths", "edgecolors", + 'facecolor']) # alias for c @docstring.dedent_interpd def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, @@ -3867,7 +3867,7 @@ def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, return collection - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x", "y"]) @docstring.dedent_interpd def hexbin(self, x, y, C=None, gridsize=100, bins=None, xscale='linear', yscale='linear', extent=None, From f488bc743fc33e8c714ee81b60b6e90d0d845a7b Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Mon, 3 Aug 2015 01:16:43 +0200 Subject: [PATCH 12/60] ENH: let unpack_labeled_data handle var-length *args --- lib/matplotlib/__init__.py | 12 +++- .../tests/test_labled_data_unpacking.py | 59 ++++++++++++------- 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index aea13119573f..054ce92052bd 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1534,7 +1534,8 @@ def _replacer(data, key): return key -def unpack_labeled_data(replace_names=None, label_namer="y", positional_parameter_names=None): +def unpack_labeled_data(replace_names=None, replace_all_args=False, label_namer="y", + positional_parameter_names=None): """ A decorator to add a 'data' kwarg to any a function. The signature of the input function must include the ax argument at the first position :: @@ -1548,14 +1549,18 @@ def foo(ax, *args, **kwargs) replace_names : list of strings, optional, default: None The list of parameter names which arguments should be replaced by `data[name]`. If None, all arguments are replaced if they are included in `data`. + replace_all_args : bool, default: False + If True, all arguments in *args get replaced, even if they are not in replace_names. + NOTE: this should be used only when the order of the names depends on the number of *args. label_namer : string, optional, default: 'y' The name of the parameter which argument should be used as label, if label is not set. If None, the label keyword argument is not set. + NOTE: you MUST pass ``label_namer=None`` if the function can't handle a ``label`` kwarg! positional_parameter_names : list of strings, optional, default: None The full list of positional parameter names (including the `ax` argument at the first place and including all possible positional parameter in `*args`), in the right order. Can also include all other keyword parameter. Only needed if the wrapped function does contain - `*args` and replace_names is not None. + `*args` and (replace_names is not None or replace_all_args is False). """ if replace_names is not None: replace_names = set(replace_names) @@ -1584,7 +1589,8 @@ def param(func): # No argnames should be replaced arg_names = [] else: - assert not (positional_parameter_names is None), "Got replace_names and wrapped function uses *args, need positional_parameter_names!" + assert not (positional_parameter_names is None or not replace_all_args), \ + "Got replace_names and wrapped function uses *args, need positional_parameter_names!" # remove ax arg arg_names = positional_parameter_names[1:] diff --git a/lib/matplotlib/tests/test_labled_data_unpacking.py b/lib/matplotlib/tests/test_labled_data_unpacking.py index 27bab85c4b91..2095040b2462 100644 --- a/lib/matplotlib/tests/test_labled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labled_data_unpacking.py @@ -6,13 +6,14 @@ from .. import unpack_labeled_data + # these two get used in multiple tests, so define them here @unpack_labeled_data(replace_names=["x","y"]) def plot_func(ax, x, y, ls="x", label=None, w="xyz"): return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) -@unpack_labeled_data(replace_names=["x","y"], positional_parameter_names=["ax", "x", "y", "ls", "label", "w"]) +@unpack_labeled_data(replace_names=["x","y"], positional_parameter_names=["x", "y", "ls", "label", "w"]) def plot_func_varags(ax, *args, **kwargs): all_args = [None, None, "x", None, "xyz"] for i, v in enumerate(args): @@ -26,25 +27,6 @@ def plot_func_varags(ax, *args, **kwargs): all_funcs = [plot_func, plot_func_varags] -d# these two get used in multiple tests, so define them here -@unpack_labeled_data(replace_names=["x","y"]) -def plot_func(ax, x, y, ls="x", label=None, w="xyz"): - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) - - -@unpack_labeled_data(replace_names=["x","y"], positional_parameter_names=["ax", "x", "y", "ls", "label", "w"]) -def plot_func_varags(ax, *args, **kwargs): - all_args = [None, None, "x", None, "xyz"] - for i, v in enumerate(args): - all_args[i] = v - for i, k in enumerate(["x", "y", "ls", "label", "w"]): - if k in kwargs: - all_args[i] = kwargs[k] - x, y, ls, label, w = all_args - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) - - -all_funcs = [plot_func, plot_func_varags] def test_compiletime_checks(): """test decorator invocations -> no replacements""" @@ -112,6 +94,7 @@ def f(): # This sets a label although the function can't handle it. assert_raises(TypeError, f) + def test_function_call_without_data(): """test without data -> no replacements""" for func in all_funcs: @@ -192,13 +175,18 @@ def func_varags_replace_all(ax, *args, **kwargs): return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) # in the first case, we can't get a "y" argument, as we don't know the names of the *args - assert_equal(func_varags_replace_all(None, "a","b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") assert_equal(func_varags_replace_all(None, x="a",y="b", w="x", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") assert_equal(func_varags_replace_all(None, "a","b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") assert_equal(func_varags_replace_all(None, "a","b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") assert_equal(func_varags_replace_all(None, x="a",y="b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") assert_equal(func_varags_replace_all(None, x="a",y="b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + try: + from pandas.util.testing import assert_produces_warning + assert_equal(func_varags_replace_all(None, "a","b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") + except ImportError: + pass + def test_no_label_replacements(): """Test without "label_namer=None" -> no label replacement at all""" @@ -213,6 +201,7 @@ def func_no_label(ax, x, y, ls="x", label=None, w="xyz"): assert_equal(func_no_label(None, "a","b", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") assert_equal(func_no_label(None, "a","b", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + def test_more_args_than_pos_parameter(): @unpack_labeled_data(replace_names=["x","y"]) def func(ax, x, y, z=1): @@ -222,3 +211,31 @@ def func(ax, x, y, z=1): def f(): func(None, "a","b", "z", "z", data=data) assert_raises(RuntimeError, f) + + +def test_function_call_with_replace_all_args(): + """Test with a "replace_all_args" argument, all *args should be replaced""" + data = {"a":[1,2],"b":[8,9], "x":"xyz"} + + def funcy(ax, *args, **kwargs): + all_args = [None, None, "x", None, "NOT"] + for i, v in enumerate(args): + all_args[i] = v + for i, k in enumerate(["x", "y", "ls", "label", "w"]): + if k in kwargs: + all_args[i] = kwargs[k] + x, y, ls, label, w = all_args + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) + + func = unpack_labeled_data(replace_all_args=True, replace_names=["w"])(funcy) + + #assert_equal(func(None, "a","b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") + assert_equal(func(None, "a","b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func(None, "a","b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + + func2 = unpack_labeled_data(replace_all_args=True, replace_names=["w"], + positional_parameter_names=["x", "y", "ls", "label", "w"])(funcy) + + assert_equal(func2(None, "a","b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func2(None, "a","b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func2(None, "a","b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") From 1543cdeb42af73fb170d97043fdd6636d0280181 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Mon, 3 Aug 2015 01:16:55 +0200 Subject: [PATCH 13/60] ENH: proper arguments for labeled data decorator, part 2 --- lib/matplotlib/axes/_axes.py | 79 +++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index d6a3ee7f0980..71610a44c9d3 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -981,7 +981,7 @@ def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', return coll - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x", "ymin", "ymax", "colors"], label_namer="x") @docstring.dedent_interpd def vlines(self, x, ymin, ymax, colors='k', linestyles='solid', label='', **kwargs): @@ -1062,7 +1062,7 @@ def vlines(self, x, ymin, ymax, colors='k', linestyles='solid', return coll - @unpack_labeled_data() + @unpack_labeled_data(replace_all_args=False, label_namer=None) @docstring.dedent_interpd def eventplot(self, positions, orientation='horizontal', lineoffsets=1, linelengths=1, linewidths=None, colors=None, @@ -1245,7 +1245,7 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, return colls #### Basic plotting - @unpack_labeled_data() + @unpack_labeled_data(replace_all_args=True, label_namer=None) @docstring.dedent_interpd def plot(self, *args, **kwargs): """ @@ -1390,7 +1390,7 @@ def plot(self, *args, **kwargs): self.autoscale_view(scalex=scalex, scaley=scaley) return lines - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x", "y"], label_namer="y") @docstring.dedent_interpd def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False, **kwargs): @@ -1464,7 +1464,7 @@ def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False, return ret - @unpack_labeled_data() + # @unpack_labeled_data() # let 'plot' do the unpacking.. @docstring.dedent_interpd def loglog(self, *args, **kwargs): """ @@ -1526,7 +1526,7 @@ def loglog(self, *args, **kwargs): return l - @unpack_labeled_data() + # @unpack_labeled_data() # let 'plot' do the unpacking.. @docstring.dedent_interpd def semilogx(self, *args, **kwargs): """ @@ -1579,7 +1579,7 @@ def semilogx(self, *args, **kwargs): self._hold = b # restore the hold return l - @unpack_labeled_data() + # @unpack_labeled_data() # let 'plot' do the unpacking.. @docstring.dedent_interpd def semilogy(self, *args, **kwargs): """ @@ -1632,7 +1632,7 @@ def semilogy(self, *args, **kwargs): return l - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x"], label_namer="x") @docstring.dedent_interpd def acorr(self, x, **kwargs): """ @@ -1694,7 +1694,7 @@ def acorr(self, x, **kwargs): """ return self.xcorr(x, x, **kwargs) - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x", "y"], label_namer="y") @docstring.dedent_interpd def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none, usevlines=True, maxlags=10, **kwargs): @@ -1784,7 +1784,7 @@ def xcorr(self, x, y, normed=True, detrend=mlab.detrend_none, #### Specialized plotting - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x", "y"], label_namer="y") def step(self, x, y, *args, **kwargs): """ Make a step plot. @@ -1822,7 +1822,9 @@ def step(self, x, y, *args, **kwargs): return self.plot(x, y, *args, **kwargs) - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["left", "height", "width", "bottom", "color", "edgecolor", + "linewidth", "tick_label", "xerr", "yerr", "ecolor"], + label_namer=None) @docstring.dedent_interpd def bar(self, left, height, width=0.8, bottom=None, **kwargs): """ @@ -2249,7 +2251,7 @@ def barh(self, bottom, width, height=0.8, left=None, **kwargs): bottom=bottom, orientation='horizontal', **kwargs) return patches - @unpack_labeled_data() + # @unpack_labeled_data() # not df["name"] getable... @docstring.dedent_interpd def broken_barh(self, xranges, yrange, **kwargs): """ @@ -2302,7 +2304,7 @@ def broken_barh(self, xranges, yrange, **kwargs): return col - @unpack_labeled_data() + @unpack_labeled_data(replace_all_args=True, label_namer=None) def stem(self, *args, **kwargs): """ Create a stem plot. @@ -2610,7 +2612,7 @@ def pie(self, x, explode=None, labels=None, colors=None, else: return slices, texts, autotexts - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x", "y", "xerr", "yerr",], label_namer="y") @docstring.dedent_interpd def errorbar(self, x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, capsize=None, @@ -4368,7 +4370,7 @@ def quiverkey(self, *args, **kw): return qk quiverkey.__doc__ = mquiver.QuiverKey.quiverkey_doc - @unpack_labeled_data() + @unpack_labeled_data(replace_all_args=True, label_namer=None) def quiver(self, *args, **kw): if not self._hold: self.cla() @@ -4379,12 +4381,12 @@ def quiver(self, *args, **kw): return q quiver.__doc__ = mquiver.Quiver.quiver_doc - @unpack_labeled_data() + @unpack_labeled_data(replace_all_args=True, label_namer=None) def stackplot(self, x, *args, **kwargs): return mstack.stackplot(self, x, *args, **kwargs) stackplot.__doc__ = mstack.stackplot.__doc__ - @unpack_labeled_data() + #@unpack_labeled_data() # doesn't really work, as x and y have different shapes def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None, cmap=None, norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, transform=None, zorder=1, start_points=None): @@ -4405,7 +4407,7 @@ def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None, return stream_container streamplot.__doc__ = mstream.streamplot.__doc__ - @unpack_labeled_data() + @unpack_labeled_data(replace_all_args=True, label_namer=None) @docstring.dedent_interpd def barbs(self, *args, **kw): """ @@ -4422,7 +4424,8 @@ def barbs(self, *args, **kw): self.autoscale_view() return b - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x", "y"], label_namer=None, + positional_parameter_names=["x", "y", "c"]) @docstring.dedent_interpd def fill(self, *args, **kwargs): """ @@ -4474,7 +4477,7 @@ def fill(self, *args, **kwargs): self.autoscale_view() return patches - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x", "y1", "y2", "where"], label_namer=None) @docstring.dedent_interpd def fill_between(self, x, y1, y2=0, where=None, interpolate=False, step=None, @@ -4628,7 +4631,7 @@ def get_interp_point(ind): self.autoscale_view() return collection - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["y", "x1", "x2", "where"], label_namer=None) @docstring.dedent_interpd def fill_betweenx(self, y, x1, x2=0, where=None, step=None, **kwargs): @@ -4753,7 +4756,7 @@ def fill_betweenx(self, y, x1, x2=0, where=None, return collection #### plotting z(x,y): imshow, pcolor and relatives, contour - @unpack_labeled_data() + #@unpack_labeled_data() # nothing which could be in an DataFrame @docstring.dedent_interpd def imshow(self, X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, vmin=None, vmax=None, @@ -4958,7 +4961,7 @@ def _pcolorargs(funcname, *args, **kw): C = C[:Ny - 1, :Nx - 1] return X, Y, C - @unpack_labeled_data() + #@unpack_labeled_data() # 2d data can't be df["name"]'ed @docstring.dedent_interpd def pcolor(self, *args, **kwargs): """ @@ -5235,7 +5238,7 @@ def pcolor(self, *args, **kwargs): self.add_collection(collection, autolim=False) return collection - @unpack_labeled_data() + #@unpack_labeled_data() # 2d data can't be df["name"]'ed @docstring.dedent_interpd def pcolormesh(self, *args, **kwargs): """ @@ -5384,7 +5387,7 @@ def pcolormesh(self, *args, **kwargs): self.add_collection(collection, autolim=False) return collection - @unpack_labeled_data() + #@unpack_labeled_data() #2d data can't be df["name"]'ed @docstring.dedent_interpd def pcolorfast(self, *args, **kwargs): """ @@ -5572,7 +5575,7 @@ def pcolorfast(self, *args, **kwargs): self.autoscale_view(tight=True) return ret - @unpack_labeled_data() + #@unpack_labeled_data() # takes 2d data :-( def contour(self, *args, **kwargs): if not self._hold: self.cla() @@ -5580,7 +5583,7 @@ def contour(self, *args, **kwargs): return mcontour.QuadContourSet(self, *args, **kwargs) contour.__doc__ = mcontour.QuadContourSet.contour_doc - @unpack_labeled_data() + #@unpack_labeled_data() # takes 2d data :-( def contourf(self, *args, **kwargs): if not self._hold: self.cla() @@ -5621,7 +5624,7 @@ def table(self, **kwargs): #### Data analysis - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x"], label_namer="x") @docstring.dedent_interpd def hist(self, x, bins=10, range=None, normed=False, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', @@ -6168,7 +6171,7 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, else: return n, bins, cbook.silent_list('Lists of Patches', patches) - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x","y", "weights"], label_namer=None) @docstring.dedent_interpd def hist2d(self, x, y, bins=10, range=None, normed=False, weights=None, cmin=None, cmax=None, **kwargs): @@ -6262,7 +6265,7 @@ def hist2d(self, x, y, bins=10, range=None, normed=False, weights=None, return h, xedges, yedges, pc - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x"], label_namer=None) @docstring.dedent_interpd def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, @@ -6387,7 +6390,7 @@ def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, else: return pxx, freqs, line - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x", "y"], label_namer="y") @docstring.dedent_interpd def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, @@ -6499,7 +6502,7 @@ def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None, else: return pxy, freqs, line - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x"], label_namer=None) @docstring.dedent_interpd def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, scale=None, @@ -6599,7 +6602,7 @@ def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x"], label_namer=None) @docstring.dedent_interpd def angle_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, **kwargs): @@ -6677,7 +6680,7 @@ def angle_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x"], label_namer=None) @docstring.dedent_interpd def phase_spectrum(self, x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, **kwargs): @@ -6755,7 +6758,7 @@ def phase_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x","y"], label_namer=None) @docstring.dedent_interpd def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, window=mlab.window_hanning, noverlap=0, pad_to=None, @@ -6823,7 +6826,7 @@ def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, return cxy, freqs - @unpack_labeled_data() + @unpack_labeled_data(replace_names=["x"], label_namer=None) @docstring.dedent_interpd def specgram(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, @@ -7141,7 +7144,7 @@ def matshow(self, Z, **kwargs): integer=True)) return im - @unpack_labeled_data() + @unpack_labeled_data( replace_all_args=True, label_namer=None) def violinplot(self, dataset, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False, points=100, bw_method=None): @@ -7246,7 +7249,7 @@ def _kde_method(X, coords): widths=widths, showmeans=showmeans, showextrema=showextrema, showmedians=showmedians) - @unpack_labeled_data() + @unpack_labeled_data(replace_all_args=True, label_namer=None) def violin(self, vpstats, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False): """Drawing function for violin plots. From f4eea6e5bed45b66457c754a21f7fe0ba9128d21 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Mon, 3 Aug 2015 01:55:21 +0200 Subject: [PATCH 14/60] FIX: handle 'enough' information about arg names in unpack_labeled_data --- lib/matplotlib/__init__.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 054ce92052bd..43517e70a669 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1588,9 +1588,19 @@ def param(func): elif len(replace_names) == 0: # No argnames should be replaced arg_names = [] + elif len(_arg_names) > 1 and (positional_parameter_names is None): + # we got no manual parameter names but more than an 'ax' ... + if len(set(replace_names) - set(_arg_names[1:])) == 0: + # all to be replaced arguments are in the list + arg_names = _arg_names[1:] + else: + msg = "Got unknown 'replace_names' and wrapped function uses '*args', " \ + "need 'positional_parameter_names'!" + raise AssertionError(msg) else: assert not (positional_parameter_names is None or not replace_all_args), \ - "Got replace_names and wrapped function uses *args, need positional_parameter_names!" + "Got 'replace_names' and wrapped function uses '*args', " \ + "need 'positional_parameter_names'!" # remove ax arg arg_names = positional_parameter_names[1:] From c4a6044e1308e258b55953d04fcd7d7cccdeb3a3 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Mon, 3 Aug 2015 17:57:10 +0200 Subject: [PATCH 15/60] FIX: unbreak another corner case in unpack_labeled_data --- lib/matplotlib/__init__.py | 38 +++++++++++-------- .../tests/test_labled_data_unpacking.py | 5 ++- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 43517e70a669..2fd81b7bbbd8 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1557,10 +1557,10 @@ def foo(ax, *args, **kwargs) None, the label keyword argument is not set. NOTE: you MUST pass ``label_namer=None`` if the function can't handle a ``label`` kwarg! positional_parameter_names : list of strings, optional, default: None - The full list of positional parameter names (including the `ax` argument at the first place - and including all possible positional parameter in `*args`), in the right order. Can also - include all other keyword parameter. Only needed if the wrapped function does contain - `*args` and (replace_names is not None or replace_all_args is False). + The full list of positional parameter names (excluding an explicit `ax`/'self' argument at + the first place and including all possible positional parameter in `*args`), in the right + order. Can also include all other keyword parameter. Only needed if the wrapped function + does contain `*args` and (replace_names is not None or replace_all_args is False). """ if replace_names is not None: replace_names = set(replace_names) @@ -1577,7 +1577,7 @@ def param(func): # in **kwargs, so we only need to check for varargs: # http://stupidpythonideas.blogspot.de/2013/08/arguments-and-parameters.html if _has_no_varargs: - # remove the first "ax" arg + # remove the first "ax" / self arg arg_names = _arg_names[1:] else: # in this case we need a supplied list of arguments or we need to replace all variables @@ -1594,15 +1594,18 @@ def param(func): # all to be replaced arguments are in the list arg_names = _arg_names[1:] else: - msg = "Got unknown 'replace_names' and wrapped function uses '*args', " \ + msg = "Got unknown 'replace_names' and wrapped function '%s' uses '*args', " \ "need 'positional_parameter_names'!" - raise AssertionError(msg) + raise AssertionError(msg % func.__name__) else: - assert not (positional_parameter_names is None or not replace_all_args), \ - "Got 'replace_names' and wrapped function uses '*args', " \ - "need 'positional_parameter_names'!" - # remove ax arg - arg_names = positional_parameter_names[1:] + if not positional_parameter_names is None: + arg_names = positional_parameter_names + else: + if replace_all_args: + arg_names=[] + else: + msg = "Got 'replace_names' and wrapped function '%s' uses *args, need 'positional_parameter_names' or 'replace_all_args'!" + raise AssertionError(msg % func.__name__) # compute the possible label_namer and label position in positional arguments label_pos = 9999 # bigger than all "possible" argument lists @@ -1640,16 +1643,19 @@ def inner(ax, *args, **kwargs): if not isinstance(label, six.string_types): label = None - if replace_names is None: + if (replace_names is None) or replace_all_args: # all should be replaced args = tuple(_replacer(data, a) for j, a in enumerate(args)) - kwargs = dict((k, _replacer(data, v)) for k, v in six.iteritems(kwargs)) else: # An arg is replaced if the arg_name of that position is in replace_names ... if len(arg_names) < len(args): raise RuntimeError("Got more args than function expects") args = tuple(_replacer(data, a) if arg_names[j] in replace_names else a for j, a in enumerate(args)) + + if replace_names is None: + kwargs = dict((k, _replacer(data, v)) for k, v in six.iteritems(kwargs)) + else: # ... or a kwarg of that name in replace_names kwargs = dict((k, _replacer(data, v) if k in replace_names else v) for k, v in six.iteritems(kwargs)) @@ -1673,9 +1679,9 @@ def inner(ax, *args, **kwargs): kwargs['label'] = label else: import warnings - msg = "Tried to set a label via parameter '%s' but couldn't find such an argument. \n"\ + msg = "Tried to set a label via parameter '%s' in func '%s' but couldn't find such an argument. \n"\ "(This is a programming error, please report to the matplotlib list!)" - warnings.warn(msg, RuntimeWarning, stacklevel=2) + warnings.warn(msg % (label_namer, func.__name__), RuntimeWarning, stacklevel=2) #raise Exception() return func(ax, *args, **kwargs) return inner diff --git a/lib/matplotlib/tests/test_labled_data_unpacking.py b/lib/matplotlib/tests/test_labled_data_unpacking.py index 2095040b2462..204a26dc5b2c 100644 --- a/lib/matplotlib/tests/test_labled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labled_data_unpacking.py @@ -38,10 +38,13 @@ def func_no_ax_args(*args, **kwargs): pass # this is ok unpack_labeled_data(replace_names=["x","y"])(func) unpack_labeled_data(replace_names=["x","y"])(func_kwargs) + # this has "enough" information to do all the replaces + unpack_labeled_data(replace_names=["x","y"])(func_args) # no positional_parameter_names but needed due to replaces def f(): - unpack_labeled_data(replace_names=["x","y"])(func_args) + # z is unknown + unpack_labeled_data(replace_names=["x","y", "z"])(func_args) assert_raises(AssertionError, f) def f(): unpack_labeled_data(replace_names=["x","y"])(func_no_ax_args) From 4d1ffb8500eb43ca59382f78c0fe7a9927486bd4 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Mon, 3 Aug 2015 20:26:31 +0200 Subject: [PATCH 16/60] FIX: fix unwarranted label argument for pie() with unpack_labeled_data --- lib/matplotlib/axes/_axes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 71610a44c9d3..2af66c692ec2 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -2392,7 +2392,7 @@ def stem(self, *args, **kwargs): return stem_container - @unpack_labeled_data(replace_names=['x', 'labels', 'colors'], label_namer="x") + @unpack_labeled_data(replace_names=['x', 'labels', 'colors'], label_namer=None) def pie(self, x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, radius=None, counterclock=True, From c78bb9255937cc61e812e5fba462636837d5572d Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Mon, 3 Aug 2015 22:45:10 +0200 Subject: [PATCH 17/60] FIX: decorate funcs which take 2d data --- lib/matplotlib/axes/_axes.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 2af66c692ec2..397d2772d159 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -902,7 +902,7 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs): self.autoscale_view(scaley=False) return p - @unpack_labeled_data(replace_names=['y', 'xmin', 'xmax']) + @unpack_labeled_data(replace_names=['y', 'xmin', 'xmax'], label_namer="y") @docstring.dedent def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', label='', **kwargs): @@ -2251,7 +2251,7 @@ def barh(self, bottom, width, height=0.8, left=None, **kwargs): bottom=bottom, orientation='horizontal', **kwargs) return patches - # @unpack_labeled_data() # not df["name"] getable... + @unpack_labeled_data(label_namer=None) @docstring.dedent_interpd def broken_barh(self, xranges, yrange, **kwargs): """ @@ -3661,7 +3661,8 @@ def dopatch(xs, ys, **kwargs): medians=medians, fliers=fliers, means=means) @unpack_labeled_data(replace_names=["x", "y", "s", "c", "linewidths", "edgecolors", - 'facecolor']) # alias for c + 'facecolor', 'facecolors', 'color'], # alias for c + label_namer="y") @docstring.dedent_interpd def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, @@ -3869,7 +3870,7 @@ def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, return collection - @unpack_labeled_data(replace_names=["x", "y"]) + @unpack_labeled_data(replace_names=["x", "y"], label_namer="y") @docstring.dedent_interpd def hexbin(self, x, y, C=None, gridsize=100, bins=None, xscale='linear', yscale='linear', extent=None, @@ -4386,7 +4387,7 @@ def stackplot(self, x, *args, **kwargs): return mstack.stackplot(self, x, *args, **kwargs) stackplot.__doc__ = mstack.stackplot.__doc__ - #@unpack_labeled_data() # doesn't really work, as x and y have different shapes + @unpack_labeled_data(replace_names=["x", "y", "u", "v", "start_points"], label_namer=None) def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None, cmap=None, norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, transform=None, zorder=1, start_points=None): @@ -4756,7 +4757,7 @@ def fill_betweenx(self, y, x1, x2=0, where=None, return collection #### plotting z(x,y): imshow, pcolor and relatives, contour - #@unpack_labeled_data() # nothing which could be in an DataFrame + @unpack_labeled_data(label_namer=None) @docstring.dedent_interpd def imshow(self, X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, vmin=None, vmax=None, @@ -4961,7 +4962,7 @@ def _pcolorargs(funcname, *args, **kw): C = C[:Ny - 1, :Nx - 1] return X, Y, C - #@unpack_labeled_data() # 2d data can't be df["name"]'ed + @unpack_labeled_data(label_namer=None) @docstring.dedent_interpd def pcolor(self, *args, **kwargs): """ @@ -5238,7 +5239,7 @@ def pcolor(self, *args, **kwargs): self.add_collection(collection, autolim=False) return collection - #@unpack_labeled_data() # 2d data can't be df["name"]'ed + @unpack_labeled_data(label_namer=None) @docstring.dedent_interpd def pcolormesh(self, *args, **kwargs): """ @@ -5387,7 +5388,7 @@ def pcolormesh(self, *args, **kwargs): self.add_collection(collection, autolim=False) return collection - #@unpack_labeled_data() #2d data can't be df["name"]'ed + @unpack_labeled_data(label_namer=None) @docstring.dedent_interpd def pcolorfast(self, *args, **kwargs): """ From d3331ca82305fdbc93e7caaf1e4e1a65da96af5f Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Mon, 3 Aug 2015 22:45:46 +0200 Subject: [PATCH 18/60] ENH: change 'label_namer' default to None None seems the be used much more often than 'y' --- lib/matplotlib/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 2fd81b7bbbd8..c7a66e1c3587 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1534,7 +1534,7 @@ def _replacer(data, key): return key -def unpack_labeled_data(replace_names=None, replace_all_args=False, label_namer="y", +def unpack_labeled_data(replace_names=None, replace_all_args=False, label_namer=None, positional_parameter_names=None): """ A decorator to add a 'data' kwarg to any a function. The signature @@ -1552,10 +1552,9 @@ def foo(ax, *args, **kwargs) replace_all_args : bool, default: False If True, all arguments in *args get replaced, even if they are not in replace_names. NOTE: this should be used only when the order of the names depends on the number of *args. - label_namer : string, optional, default: 'y' + label_namer : string, optional, default: None The name of the parameter which argument should be used as label, if label is not set. If None, the label keyword argument is not set. - NOTE: you MUST pass ``label_namer=None`` if the function can't handle a ``label`` kwarg! positional_parameter_names : list of strings, optional, default: None The full list of positional parameter names (excluding an explicit `ax`/'self' argument at the first place and including all possible positional parameter in `*args`), in the right From ea3e22dfc1f3f2694613649f36bdda7ddb107fee Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Mon, 3 Aug 2015 23:00:02 +0200 Subject: [PATCH 19/60] PEP8: fix some of the PEP8 problems in new code Not done: long lines between 80...100 Test code was auto-reformatted in pycharm with max line length 100 --- lib/matplotlib/__init__.py | 77 ++++--- lib/matplotlib/axes/_axes.py | 10 +- .../tests/test_labled_data_unpacking.py | 215 +++++++++++------- 3 files changed, 188 insertions(+), 114 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index c7a66e1c3587..9a23cb503faf 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1534,8 +1534,8 @@ def _replacer(data, key): return key -def unpack_labeled_data(replace_names=None, replace_all_args=False, label_namer=None, - positional_parameter_names=None): +def unpack_labeled_data(replace_names=None, replace_all_args=False, + label_namer=None, positional_parameter_names=None): """ A decorator to add a 'data' kwarg to any a function. The signature of the input function must include the ax argument at the first position :: @@ -1547,19 +1547,24 @@ def foo(ax, *args, **kwargs) Parameters ---------- replace_names : list of strings, optional, default: None - The list of parameter names which arguments should be replaced by `data[name]`. If None, - all arguments are replaced if they are included in `data`. + The list of parameter names which arguments should be replaced by + `data[name]`. If None, all arguments are replaced if they are + included in `data`. replace_all_args : bool, default: False - If True, all arguments in *args get replaced, even if they are not in replace_names. - NOTE: this should be used only when the order of the names depends on the number of *args. + If True, all arguments in *args get replaced, even if they are not + in replace_names. + NOTE: this should be used only when the order of the names depends on + the number of *args. label_namer : string, optional, default: None - The name of the parameter which argument should be used as label, if label is not set. If - None, the label keyword argument is not set. + The name of the parameter which argument should be used as label, if + label is not set. If None, the label keyword argument is not set. positional_parameter_names : list of strings, optional, default: None - The full list of positional parameter names (excluding an explicit `ax`/'self' argument at - the first place and including all possible positional parameter in `*args`), in the right - order. Can also include all other keyword parameter. Only needed if the wrapped function - does contain `*args` and (replace_names is not None or replace_all_args is False). + The full list of positional parameter names (excluding an explicit + `ax`/'self' argument at the first place and including all possible + positional parameter in `*args`), in the right order. Can also include + all other keyword parameter. Only needed if the wrapped function does + contain `*args` and (replace_names is not None or replace_all_args is + False). """ if replace_names is not None: replace_names = set(replace_names) @@ -1570,10 +1575,11 @@ def param(func): arg_spec = inspect.getargspec(func) _arg_names = arg_spec.args _has_no_varargs = arg_spec.varargs is None - _has_varkwargs = not arg_spec.keywords is None + _has_varkwargs = arg_spec.keywords is not None - # there can't be any positional arguments behind *args and no positional args can end up - # in **kwargs, so we only need to check for varargs: + # there can't be any positional arguments behind *args and no + # positional args can end up in **kwargs, so we only need to check for + # varargs: # http://stupidpythonideas.blogspot.de/2013/08/arguments-and-parameters.html if _has_no_varargs: # remove the first "ax" / self arg @@ -1597,32 +1603,36 @@ def param(func): "need 'positional_parameter_names'!" raise AssertionError(msg % func.__name__) else: - if not positional_parameter_names is None: + if positional_parameter_names is not None: arg_names = positional_parameter_names else: if replace_all_args: - arg_names=[] + arg_names = [] else: - msg = "Got 'replace_names' and wrapped function '%s' uses *args, need 'positional_parameter_names' or 'replace_all_args'!" + msg = "Got 'replace_names' and wrapped function '%s' uses *args, " \ + "need 'positional_parameter_names' or 'replace_all_args'!" raise AssertionError(msg % func.__name__) # compute the possible label_namer and label position in positional arguments - label_pos = 9999 # bigger than all "possible" argument lists - label_namer_pos = 9999 # bigger than all "possible" argument lists + label_pos = 9999 # bigger than all "possible" argument lists + label_namer_pos = 9999 # bigger than all "possible" argument lists if label_namer and arg_names and (label_namer in arg_names): label_namer_pos = arg_names.index(label_namer) if "label" in arg_names: label_pos = arg_names.index("label") - # Check the case we know a label_namer but we can't find it the arg_names... - # Unfortunately the label_namer can be in **kwargs, which we can't detect here and which - # results in a non-set label which might surprise the user :-( + # Check the case we know a label_namer but we can't find it the + # arg_names... Unfortunately the label_namer can be in **kwargs, + # which we can't detect here and which results in a non-set label + # which might surprise the user :-( if label_namer and not _has_varkwargs: if not arg_names: - msg = "label_namer '%s' can't be found as the parameter without 'positional_parameter_names'." - raise AssertionError(msg % (label_namer)) + msg = "label_namer '%s' can't be found as the parameter without " \ + "'positional_parameter_names'." + raise AssertionError(msg % label_namer) elif label_namer not in arg_names: - msg = "label_namer '%s' can't be found in the parameter names (known argnames: %s)." + msg = "label_namer '%s' can't be found in the parameter names " \ + "(known argnames: %s)." raise AssertionError(msg % (label_namer, arg_names)) else: # this is the case when the name is in arg_names @@ -1662,8 +1672,8 @@ def inner(ax, *args, **kwargs): # replace the label if this func "wants" a label arg and the user didn't set one # Note: if the usere puts in "label=None", it does *NOT* get replaced! user_supplied_label = ( - (len(args) >= label_pos) or # label is included in args - ('label' in kwargs) # ... or in kwargs + (len(args) >= label_pos) or # label is included in args + ('label' in kwargs) # ... or in kwargs ) if (label_namer and not user_supplied_label): if label_namer_pos < len(args): @@ -1678,10 +1688,13 @@ def inner(ax, *args, **kwargs): kwargs['label'] = label else: import warnings - msg = "Tried to set a label via parameter '%s' in func '%s' but couldn't find such an argument. \n"\ - "(This is a programming error, please report to the matplotlib list!)" - warnings.warn(msg % (label_namer, func.__name__), RuntimeWarning, stacklevel=2) - #raise Exception() + msg = "Tried to set a label via parameter '%s' in " \ + "func '%s' but couldn't find such an argument. \n"\ + "(This is a programming error, please report to the " \ + "matplotlib list!)" + warnings.warn(msg % (label_namer, func.__name__), + RuntimeWarning, stacklevel=2) + # raise Exception() return func(ax, *args, **kwargs) return inner return param diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 397d2772d159..d508604fad2a 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -2612,7 +2612,7 @@ def pie(self, x, explode=None, labels=None, colors=None, else: return slices, texts, autotexts - @unpack_labeled_data(replace_names=["x", "y", "xerr", "yerr",], label_namer="y") + @unpack_labeled_data(replace_names=["x", "y", "xerr", "yerr"], label_namer="y") @docstring.dedent_interpd def errorbar(self, x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, capsize=None, @@ -3661,7 +3661,7 @@ def dopatch(xs, ys, **kwargs): medians=medians, fliers=fliers, means=means) @unpack_labeled_data(replace_names=["x", "y", "s", "c", "linewidths", "edgecolors", - 'facecolor', 'facecolors', 'color'], # alias for c + 'facecolor', 'facecolors', 'color'], # alias for c label_namer="y") @docstring.dedent_interpd def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, @@ -6172,7 +6172,7 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, else: return n, bins, cbook.silent_list('Lists of Patches', patches) - @unpack_labeled_data(replace_names=["x","y", "weights"], label_namer=None) + @unpack_labeled_data(replace_names=["x", "y", "weights"], label_namer=None) @docstring.dedent_interpd def hist2d(self, x, y, bins=10, range=None, normed=False, weights=None, cmin=None, cmax=None, **kwargs): @@ -6759,7 +6759,7 @@ def phase_spectrum(self, x, Fs=None, Fc=None, window=None, return spec, freqs, lines[0] - @unpack_labeled_data(replace_names=["x","y"], label_namer=None) + @unpack_labeled_data(replace_names=["x", "y"], label_namer=None) @docstring.dedent_interpd def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, window=mlab.window_hanning, noverlap=0, pad_to=None, @@ -7145,7 +7145,7 @@ def matshow(self, Z, **kwargs): integer=True)) return im - @unpack_labeled_data( replace_all_args=True, label_namer=None) + @unpack_labeled_data(replace_all_args=True, label_namer=None) def violinplot(self, dataset, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False, points=100, bw_method=None): diff --git a/lib/matplotlib/tests/test_labled_data_unpacking.py b/lib/matplotlib/tests/test_labled_data_unpacking.py index 204a26dc5b2c..3994bd2b1de5 100644 --- a/lib/matplotlib/tests/test_labled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labled_data_unpacking.py @@ -8,12 +8,13 @@ # these two get used in multiple tests, so define them here -@unpack_labeled_data(replace_names=["x","y"]) +@unpack_labeled_data(replace_names=["x", "y"]) def plot_func(ax, x, y, ls="x", label=None, w="xyz"): - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x), list(y), ls, w, label) -@unpack_labeled_data(replace_names=["x","y"], positional_parameter_names=["x", "y", "ls", "label", "w"]) +@unpack_labeled_data(replace_names=["x", "y"], + positional_parameter_names=["x", "y", "ls", "label", "w"]) def plot_func_varags(ax, *args, **kwargs): all_args = [None, None, "x", None, "xyz"] for i, v in enumerate(args): @@ -22,7 +23,7 @@ def plot_func_varags(ax, *args, **kwargs): if k in kwargs: all_args[i] = kwargs[k] x, y, ls, label, w = all_args - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x), list(y), ls, w, label) all_funcs = [plot_func, plot_func_varags] @@ -30,24 +31,31 @@ def plot_func_varags(ax, *args, **kwargs): def test_compiletime_checks(): """test decorator invocations -> no replacements""" - def func(ax, x,y): pass - def func_args(ax, x,y,*args): pass - def func_kwargs(ax, x,y,**kwargs): pass + + def func(ax, x, y): pass + + def func_args(ax, x, y, *args): pass + + def func_kwargs(ax, x, y, **kwargs): pass + def func_no_ax_args(*args, **kwargs): pass # this is ok - unpack_labeled_data(replace_names=["x","y"])(func) - unpack_labeled_data(replace_names=["x","y"])(func_kwargs) + unpack_labeled_data(replace_names=["x", "y"])(func) + unpack_labeled_data(replace_names=["x", "y"])(func_kwargs) # this has "enough" information to do all the replaces - unpack_labeled_data(replace_names=["x","y"])(func_args) + unpack_labeled_data(replace_names=["x", "y"])(func_args) # no positional_parameter_names but needed due to replaces def f(): # z is unknown - unpack_labeled_data(replace_names=["x","y", "z"])(func_args) + unpack_labeled_data(replace_names=["x", "y", "z"])(func_args) + assert_raises(AssertionError, f) + def f(): - unpack_labeled_data(replace_names=["x","y"])(func_no_ax_args) + unpack_labeled_data(replace_names=["x", "y"])(func_no_ax_args) + assert_raises(AssertionError, f) # no replacements at all -> all ok... @@ -59,9 +67,12 @@ def f(): # label namer is unknown def f(): unpack_labeled_data(label_namer="z")(func) + assert_raises(AssertionError, f) + def f(): unpack_labeled_data(label_namer="z")(func_args) + assert_raises(AssertionError, f) # but "ok-ish", if func has kwargs -> will show up at runtime :-( unpack_labeled_data(label_namer="z")(func_kwargs) @@ -76,10 +87,12 @@ def test_label_problems_at_runtime(): raise SkipTest("Pandas not installed") @unpack_labeled_data(label_namer="z") - def func(*args, **kwargs):pass + def func(*args, **kwargs): + pass def f(): func(None, x="a", y="b") + # This is a programming mistake: the parameter which should add the # label is not present in the function call. Unfortunately this was masked # due to the **kwargs useage @@ -87,13 +100,16 @@ def f(): with assert_produces_warning(RuntimeWarning): f() - def real_func(x,y):pass + def real_func(x, y): + pass + @unpack_labeled_data(label_namer="x") def func(*args, **kwargs): real_func(**kwargs) def f(): func(None, x="a", y="b") + # This sets a label although the function can't handle it. assert_raises(TypeError, f) @@ -101,36 +117,51 @@ def f(): def test_function_call_without_data(): """test without data -> no replacements""" for func in all_funcs: - assert_equal(func(None, "x","y"), "x: ['x'], y: ['y'], ls: x, w: xyz, label: None") - assert_equal(func(None, x="x",y="y") , "x: ['x'], y: ['y'], ls: x, w: xyz, label: None") - assert_equal(func(None, "x","y", label="") , "x: ['x'], y: ['y'], ls: x, w: xyz, label: ") - assert_equal(func(None, "x","y", label="text") , "x: ['x'], y: ['y'], ls: x, w: xyz, label: text") - assert_equal(func(None, x="x",y="y", label="") , "x: ['x'], y: ['y'], ls: x, w: xyz, label: ") - assert_equal(func(None, x="x",y="y", label="text") , "x: ['x'], y: ['y'], ls: x, w: xyz, label: text") + assert_equal(func(None, "x", "y"), "x: ['x'], y: ['y'], ls: x, w: xyz, label: None") + assert_equal(func(None, x="x", y="y"), "x: ['x'], y: ['y'], ls: x, w: xyz, label: None") + assert_equal(func(None, "x", "y", label=""), "x: ['x'], y: ['y'], ls: x, w: xyz, label: ") + assert_equal(func(None, "x", "y", label="text"), + "x: ['x'], y: ['y'], ls: x, w: xyz, label: text") + assert_equal(func(None, x="x", y="y", label=""), + "x: ['x'], y: ['y'], ls: x, w: xyz, label: ") + assert_equal(func(None, x="x", y="y", label="text"), + "x: ['x'], y: ['y'], ls: x, w: xyz, label: text") def test_function_call_with_dict_data(): """Test with dict data -> label comes from the value of 'x' parameter """ - data = {"a":[1,2],"b":[8,9], "w":"NOT"} + data = {"a": [1, 2], "b": [8, 9], "w": "NOT"} for func in all_funcs: - assert_equal(func(None, "a","b", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") - assert_equal(func(None, x="a",y="b", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") - assert_equal(func(None, "a","b", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func(None, "a","b", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") - assert_equal(func(None, x="a",y="b", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func(None, x="a",y="b", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal(func(None, "a", "b", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func(None, x="a", y="b", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func(None, "a", "b", label="", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func(None, "a", "b", label="text", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal(func(None, x="a", y="b", label="", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func(None, x="a", y="b", label="text", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") def test_function_call_with_dict_data_not_in_data(): "test for the case that one var is not in data -> half replaces, half kept" - data = {"a":[1,2], "w":"NOT"} + data = {"a": [1, 2], "w": "NOT"} for func in all_funcs: - assert_equal(func(None, "a","b", data=data), "x: [1, 2], y: ['b'], ls: x, w: xyz, label: b") - assert_equal(func(None, x="a",y="b", data=data) , "x: [1, 2], y: ['b'], ls: x, w: xyz, label: b") - assert_equal(func(None, "a","b", label="", data=data) , "x: [1, 2], y: ['b'], ls: x, w: xyz, label: ") - assert_equal(func(None, "a","b", label="text", data=data) , "x: [1, 2], y: ['b'], ls: x, w: xyz, label: text") - assert_equal(func(None, x="a",y="b", label="", data=data) , "x: [1, 2], y: ['b'], ls: x, w: xyz, label: ") - assert_equal(func(None, x="a",y="b", label="text", data=data) , "x: [1, 2], y: ['b'], ls: x, w: xyz, label: text") + assert_equal(func(None, "a", "b", data=data), + "x: [1, 2], y: ['b'], ls: x, w: xyz, label: b") + assert_equal(func(None, x="a", y="b", data=data), + "x: [1, 2], y: ['b'], ls: x, w: xyz, label: b") + assert_equal(func(None, "a", "b", label="", data=data), + "x: [1, 2], y: ['b'], ls: x, w: xyz, label: ") + assert_equal(func(None, "a", "b", label="text", data=data), + "x: [1, 2], y: ['b'], ls: x, w: xyz, label: text") + assert_equal(func(None, x="a", y="b", label="", data=data), + "x: [1, 2], y: ['b'], ls: x, w: xyz, label: ") + assert_equal(func(None, x="a", y="b", label="text", data=data), + "x: [1, 2], y: ['b'], ls: x, w: xyz, label: text") def test_function_call_with_pandas_data(): @@ -140,31 +171,43 @@ def test_function_call_with_pandas_data(): except ImportError: raise SkipTest("Pandas not installed") - data = pd.DataFrame({"a":[1,2],"b":[8,9],"w":["NOT","NOT"]}) + data = pd.DataFrame({"a": [1, 2], "b": [8, 9], "w": ["NOT", "NOT"]}) for func in all_funcs: - assert_equal(func(None, "a","b", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") - assert_equal(func(None, x="a",y="b", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") - assert_equal(func(None, "a","b", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func(None, "a","b", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") - assert_equal(func(None, x="a",y="b", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func(None, x="a",y="b", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal(func(None, "a", "b", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func(None, x="a", y="b", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func(None, "a", "b", label="", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func(None, "a", "b", label="text", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal(func(None, x="a", y="b", label="", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func(None, x="a", y="b", label="text", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") def test_function_call_replace_all(): """Test without a "replace_names" argument, all vars should be replaced""" - data = {"a":[1,2],"b":[8,9], "x":"xyz"} + data = {"a": [1, 2], "b": [8, 9], "x": "xyz"} @unpack_labeled_data() def func_replace_all(ax, x, y, ls="x", label=None, w="NOT"): - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) - - assert_equal(func_replace_all(None, "a","b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") - assert_equal(func_replace_all(None, x="a",y="b", w="x", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") - assert_equal(func_replace_all(None, "a","b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func_replace_all(None, "a","b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") - assert_equal(func_replace_all(None, x="a",y="b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func_replace_all(None, x="a",y="b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x), list(y), ls, w, label) + + assert_equal(func_replace_all(None, "a", "b", w="x", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func_replace_all(None, x="a", y="b", w="x", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func_replace_all(None, "a", "b", w="x", label="", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func_replace_all(None, "a", "b", w="x", label="text", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal(func_replace_all(None, x="a", y="b", w="x", label="", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func_replace_all(None, x="a", y="b", w="x", label="text", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") @unpack_labeled_data() def func_varags_replace_all(ax, *args, **kwargs): @@ -175,18 +218,25 @@ def func_varags_replace_all(ax, *args, **kwargs): if k in kwargs: all_args[i] = kwargs[k] x, y, ls, label, w = all_args - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x), list(y), ls, w, label) # in the first case, we can't get a "y" argument, as we don't know the names of the *args - assert_equal(func_varags_replace_all(None, x="a",y="b", w="x", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") - assert_equal(func_varags_replace_all(None, "a","b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func_varags_replace_all(None, "a","b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") - assert_equal(func_varags_replace_all(None, x="a",y="b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func_varags_replace_all(None, x="a",y="b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal(func_varags_replace_all(None, x="a", y="b", w="x", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func_varags_replace_all(None, "a", "b", w="x", label="", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func_varags_replace_all(None, "a", "b", w="x", label="text", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal(func_varags_replace_all(None, x="a", y="b", w="x", label="", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func_varags_replace_all(None, x="a", y="b", w="x", label="text", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") try: from pandas.util.testing import assert_produces_warning - assert_equal(func_varags_replace_all(None, "a","b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") + + assert_equal(func_varags_replace_all(None, "a", "b", w="x", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") except ImportError: pass @@ -194,31 +244,37 @@ def func_varags_replace_all(ax, *args, **kwargs): def test_no_label_replacements(): """Test without "label_namer=None" -> no label replacement at all""" - @unpack_labeled_data(replace_names=["x","y"], label_namer=None) + @unpack_labeled_data(replace_names=["x", "y"], label_namer=None) def func_no_label(ax, x, y, ls="x", label=None, w="xyz"): - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x), list(y), ls, w, label) - data = {"a":[1,2],"b":[8,9], "w":"NOT"} - assert_equal(func_no_label(None, "a","b", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") - assert_equal(func_no_label(None, x="a",y="b", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") - assert_equal(func_no_label(None, "a","b", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func_no_label(None, "a","b", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + data = {"a": [1, 2], "b": [8, 9], "w": "NOT"} + assert_equal(func_no_label(None, "a", "b", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") + assert_equal(func_no_label(None, x="a", y="b", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") + assert_equal(func_no_label(None, "a", "b", label="", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func_no_label(None, "a", "b", label="text", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") def test_more_args_than_pos_parameter(): - @unpack_labeled_data(replace_names=["x","y"]) + @unpack_labeled_data(replace_names=["x", "y"]) def func(ax, x, y, z=1): pass - data = {"a":[1,2],"b":[8,9], "w":"NOT"} + data = {"a": [1, 2], "b": [8, 9], "w": "NOT"} + def f(): - func(None, "a","b", "z", "z", data=data) + func(None, "a", "b", "z", "z", data=data) + assert_raises(RuntimeError, f) def test_function_call_with_replace_all_args(): """Test with a "replace_all_args" argument, all *args should be replaced""" - data = {"a":[1,2],"b":[8,9], "x":"xyz"} + data = {"a": [1, 2], "b": [8, 9], "x": "xyz"} def funcy(ax, *args, **kwargs): all_args = [None, None, "x", None, "NOT"] @@ -228,17 +284,22 @@ def funcy(ax, *args, **kwargs): if k in kwargs: all_args[i] = kwargs[k] x, y, ls, label, w = all_args - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x),list(y),ls, w, label) + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x), list(y), ls, w, label) func = unpack_labeled_data(replace_all_args=True, replace_names=["w"])(funcy) - #assert_equal(func(None, "a","b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") - assert_equal(func(None, "a","b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func(None, "a","b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + # assert_equal(func(None, "a","b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") + assert_equal(func(None, "a", "b", w="x", label="", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func(None, "a", "b", w="x", label="text", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") func2 = unpack_labeled_data(replace_all_args=True, replace_names=["w"], - positional_parameter_names=["x", "y", "ls", "label", "w"])(funcy) - - assert_equal(func2(None, "a","b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") - assert_equal(func2(None, "a","b", w="x", label="", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func2(None, "a","b", w="x", label="text", data=data) , "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + positional_parameter_names=["x", "y", "ls", "label", "w"])(funcy) + + assert_equal(func2(None, "a", "b", w="x", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") + assert_equal(func2(None, "a", "b", w="x", label="", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal(func2(None, "a", "b", w="x", label="text", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") From 3273673dc59c0d9b20eef5854e37d3c6f66a24b4 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 3 Aug 2015 17:43:20 -0400 Subject: [PATCH 20/60] PEP8: line wrapping fixes --- lib/matplotlib/__init__.py | 62 +++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 9a23cb503faf..a416c88b793e 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1585,8 +1585,8 @@ def param(func): # remove the first "ax" / self arg arg_names = _arg_names[1:] else: - # in this case we need a supplied list of arguments or we need to replace all variables - # -> compile time check + # in this case we need a supplied list of arguments or we need to + # replace all variables -> compile time check if replace_names is None: # all argnames should be replaced arg_names = None @@ -1599,8 +1599,9 @@ def param(func): # all to be replaced arguments are in the list arg_names = _arg_names[1:] else: - msg = "Got unknown 'replace_names' and wrapped function '%s' uses '*args', " \ - "need 'positional_parameter_names'!" + msg = ("Got unknown 'replace_names' and wrapped function " + "'%s' uses '*args', need " + "'positional_parameter_names'!") raise AssertionError(msg % func.__name__) else: if positional_parameter_names is not None: @@ -1609,11 +1610,14 @@ def param(func): if replace_all_args: arg_names = [] else: - msg = "Got 'replace_names' and wrapped function '%s' uses *args, " \ - "need 'positional_parameter_names' or 'replace_all_args'!" + msg = ("Got 'replace_names' and wrapped function " + "'%s' uses *args, need " + "'positional_parameter_names' or " + "'replace_all_args'!") raise AssertionError(msg % func.__name__) - # compute the possible label_namer and label position in positional arguments + # compute the possible label_namer and label position in positional + # arguments label_pos = 9999 # bigger than all "possible" argument lists label_namer_pos = 9999 # bigger than all "possible" argument lists if label_namer and arg_names and (label_namer in arg_names): @@ -1627,12 +1631,12 @@ def param(func): # which might surprise the user :-( if label_namer and not _has_varkwargs: if not arg_names: - msg = "label_namer '%s' can't be found as the parameter without " \ - "'positional_parameter_names'." + msg = ("label_namer '%s' can't be found as the parameter " + "without 'positional_parameter_names'.") raise AssertionError(msg % label_namer) elif label_namer not in arg_names: - msg = "label_namer '%s' can't be found in the parameter names " \ - "(known argnames: %s)." + msg = ("label_namer '%s' can't be found in the parameter " + "names (known argnames: %s).") raise AssertionError(msg % (label_namer, arg_names)) else: # this is the case when the name is in arg_names @@ -1643,7 +1647,8 @@ def inner(ax, *args, **kwargs): data = kwargs.pop('data', None) label = None if data is not None: - # save the current label_namer value so that it can be used as a label + # save the current label_namer value so that it can be used as + # a label if label_namer_pos < len(args): label = args[label_namer_pos] else: @@ -1654,23 +1659,30 @@ def inner(ax, *args, **kwargs): if (replace_names is None) or replace_all_args: # all should be replaced - args = tuple(_replacer(data, a) for j, a in enumerate(args)) + args = tuple(_replacer(data, a) for + j, a in enumerate(args)) else: - # An arg is replaced if the arg_name of that position is in replace_names ... + # An arg is replaced if the arg_name of that position is in + # replace_names ... if len(arg_names) < len(args): - raise RuntimeError("Got more args than function expects") - args = tuple(_replacer(data, a) if arg_names[j] in replace_names else a + raise RuntimeError( + "Got more args than function expects") + args = tuple(_replacer(data, a) + if arg_names[j] in replace_names else a for j, a in enumerate(args)) if replace_names is None: - kwargs = dict((k, _replacer(data, v)) for k, v in six.iteritems(kwargs)) + kwargs = dict((k, _replacer(data, v)) + for k, v in six.iteritems(kwargs)) else: # ... or a kwarg of that name in replace_names - kwargs = dict((k, _replacer(data, v) if k in replace_names else v) - for k, v in six.iteritems(kwargs)) + kwargs = dict((k, _replacer(data, v) + if k in replace_names else v) + for k, v in six.iteritems(kwargs)) - # replace the label if this func "wants" a label arg and the user didn't set one - # Note: if the usere puts in "label=None", it does *NOT* get replaced! + # replace the label if this func "wants" a label arg and the user + # didn't set one Note: if the usere puts in "label=None", it does + # *NOT* get replaced! user_supplied_label = ( (len(args) >= label_pos) or # label is included in args ('label' in kwargs) # ... or in kwargs @@ -1688,10 +1700,10 @@ def inner(ax, *args, **kwargs): kwargs['label'] = label else: import warnings - msg = "Tried to set a label via parameter '%s' in " \ - "func '%s' but couldn't find such an argument. \n"\ - "(This is a programming error, please report to the " \ - "matplotlib list!)" + msg = ("Tried to set a label via parameter '%s' in " + "func '%s' but couldn't find such an argument. \n" + "(This is a programming error, please report to " + "the matplotlib list!)") warnings.warn(msg % (label_namer, func.__name__), RuntimeWarning, stacklevel=2) # raise Exception() From 346a01416e4a1c80268ea45f459d7bf2f50d2ce1 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Tue, 4 Aug 2015 08:57:48 +0200 Subject: [PATCH 21/60] PEP8: more line length fixes --- lib/matplotlib/axes/_axes.py | 29 ++++--- .../tests/test_labled_data_unpacking.py | 80 ++++++++++++------- 2 files changed, 71 insertions(+), 38 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index d508604fad2a..04a70e1da3c0 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -981,7 +981,8 @@ def hlines(self, y, xmin, xmax, colors='k', linestyles='solid', return coll - @unpack_labeled_data(replace_names=["x", "ymin", "ymax", "colors"], label_namer="x") + @unpack_labeled_data(replace_names=["x", "ymin", "ymax", "colors"], + label_namer="x") @docstring.dedent_interpd def vlines(self, x, ymin, ymax, colors='k', linestyles='solid', label='', **kwargs): @@ -1822,8 +1823,10 @@ def step(self, x, y, *args, **kwargs): return self.plot(x, y, *args, **kwargs) - @unpack_labeled_data(replace_names=["left", "height", "width", "bottom", "color", "edgecolor", - "linewidth", "tick_label", "xerr", "yerr", "ecolor"], + @unpack_labeled_data(replace_names=["left", "height", "width", "bottom", + "color", "edgecolor", "linewidth", + "tick_label", "xerr", "yerr", + "ecolor"], label_namer=None) @docstring.dedent_interpd def bar(self, left, height, width=0.8, bottom=None, **kwargs): @@ -2392,7 +2395,8 @@ def stem(self, *args, **kwargs): return stem_container - @unpack_labeled_data(replace_names=['x', 'labels', 'colors'], label_namer=None) + @unpack_labeled_data(replace_names=['x', 'labels', 'colors'], + label_namer=None) def pie(self, x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, radius=None, counterclock=True, @@ -2612,7 +2616,8 @@ def pie(self, x, explode=None, labels=None, colors=None, else: return slices, texts, autotexts - @unpack_labeled_data(replace_names=["x", "y", "xerr", "yerr"], label_namer="y") + @unpack_labeled_data(replace_names=["x", "y", "xerr", "yerr"], + label_namer="y") @docstring.dedent_interpd def errorbar(self, x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, capsize=None, @@ -3660,8 +3665,9 @@ def dopatch(xs, ys, **kwargs): return dict(whiskers=whiskers, caps=caps, boxes=boxes, medians=medians, fliers=fliers, means=means) - @unpack_labeled_data(replace_names=["x", "y", "s", "c", "linewidths", "edgecolors", - 'facecolor', 'facecolors', 'color'], # alias for c + @unpack_labeled_data(replace_names=["x", "y", "s", "linewidths", + "edgecolors", "c", 'facecolor', + 'facecolors', 'color'], label_namer="y") @docstring.dedent_interpd def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, @@ -4387,7 +4393,8 @@ def stackplot(self, x, *args, **kwargs): return mstack.stackplot(self, x, *args, **kwargs) stackplot.__doc__ = mstack.stackplot.__doc__ - @unpack_labeled_data(replace_names=["x", "y", "u", "v", "start_points"], label_namer=None) + @unpack_labeled_data(replace_names=["x", "y", "u", "v", "start_points"], + label_namer=None) def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None, cmap=None, norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, transform=None, zorder=1, start_points=None): @@ -4478,7 +4485,8 @@ def fill(self, *args, **kwargs): self.autoscale_view() return patches - @unpack_labeled_data(replace_names=["x", "y1", "y2", "where"], label_namer=None) + @unpack_labeled_data(replace_names=["x", "y1", "y2", "where"], + label_namer=None) @docstring.dedent_interpd def fill_between(self, x, y1, y2=0, where=None, interpolate=False, step=None, @@ -4632,7 +4640,8 @@ def get_interp_point(ind): self.autoscale_view() return collection - @unpack_labeled_data(replace_names=["y", "x1", "x2", "where"], label_namer=None) + @unpack_labeled_data(replace_names=["y", "x1", "x2", "where"], + label_namer=None) @docstring.dedent_interpd def fill_betweenx(self, y, x1, x2=0, where=None, step=None, **kwargs): diff --git a/lib/matplotlib/tests/test_labled_data_unpacking.py b/lib/matplotlib/tests/test_labled_data_unpacking.py index 3994bd2b1de5..68d346b3b859 100644 --- a/lib/matplotlib/tests/test_labled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labled_data_unpacking.py @@ -10,7 +10,8 @@ # these two get used in multiple tests, so define them here @unpack_labeled_data(replace_names=["x", "y"]) def plot_func(ax, x, y, ls="x", label=None, w="xyz"): - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x), list(y), ls, w, label) + return ("x: %s, y: %s, ls: %s, w: %s, label: %s" % ( + list(x), list(y), ls, w, label)) @unpack_labeled_data(replace_names=["x", "y"], @@ -23,7 +24,8 @@ def plot_func_varags(ax, *args, **kwargs): if k in kwargs: all_args[i] = kwargs[k] x, y, ls, label, w = all_args - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x), list(y), ls, w, label) + return ("x: %s, y: %s, ls: %s, w: %s, label: %s" % ( + list(x), list(y), ls, w, label)) all_funcs = [plot_func, plot_func_varags] @@ -80,7 +82,7 @@ def f(): def test_label_problems_at_runtime(): - """These are tests for behaviour which would actually be nice to get rid of.""" + """Tests for behaviour which would actually be nice to get rid of.""" try: from pandas.util.testing import assert_produces_warning except ImportError: @@ -117,9 +119,12 @@ def f(): def test_function_call_without_data(): """test without data -> no replacements""" for func in all_funcs: - assert_equal(func(None, "x", "y"), "x: ['x'], y: ['y'], ls: x, w: xyz, label: None") - assert_equal(func(None, x="x", y="y"), "x: ['x'], y: ['y'], ls: x, w: xyz, label: None") - assert_equal(func(None, "x", "y", label=""), "x: ['x'], y: ['y'], ls: x, w: xyz, label: ") + assert_equal(func(None, "x", "y"), + "x: ['x'], y: ['y'], ls: x, w: xyz, label: None") + assert_equal(func(None, x="x", y="y"), + "x: ['x'], y: ['y'], ls: x, w: xyz, label: None") + assert_equal(func(None, "x", "y", label=""), + "x: ['x'], y: ['y'], ls: x, w: xyz, label: ") assert_equal(func(None, "x", "y", label="text"), "x: ['x'], y: ['y'], ls: x, w: xyz, label: text") assert_equal(func(None, x="x", y="y", label=""), @@ -194,7 +199,8 @@ def test_function_call_replace_all(): @unpack_labeled_data() def func_replace_all(ax, x, y, ls="x", label=None, w="NOT"): - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x), list(y), ls, w, label) + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % ( + list(x), list(y), ls, w, label) assert_equal(func_replace_all(None, "a", "b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") @@ -202,12 +208,15 @@ def func_replace_all(ax, x, y, ls="x", label=None, w="NOT"): "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") assert_equal(func_replace_all(None, "a", "b", w="x", label="", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func_replace_all(None, "a", "b", w="x", label="text", data=data), - "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") - assert_equal(func_replace_all(None, x="a", y="b", w="x", label="", data=data), - "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func_replace_all(None, x="a", y="b", w="x", label="text", data=data), - "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal( + func_replace_all(None, "a", "b", w="x", label="text", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal( + func_replace_all(None, x="a", y="b", w="x", label="", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal( + func_replace_all(None, x="a", y="b", w="x", label="text", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") @unpack_labeled_data() def func_varags_replace_all(ax, *args, **kwargs): @@ -218,19 +227,28 @@ def func_varags_replace_all(ax, *args, **kwargs): if k in kwargs: all_args[i] = kwargs[k] x, y, ls, label, w = all_args - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x), list(y), ls, w, label) + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % ( + list(x), list(y), ls, w, label) - # in the first case, we can't get a "y" argument, as we don't know the names of the *args + # in the first case, we can't get a "y" argument, + # as we don't know the names of the *args assert_equal(func_varags_replace_all(None, x="a", y="b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") - assert_equal(func_varags_replace_all(None, "a", "b", w="x", label="", data=data), - "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func_varags_replace_all(None, "a", "b", w="x", label="text", data=data), - "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") - assert_equal(func_varags_replace_all(None, x="a", y="b", w="x", label="", data=data), - "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") - assert_equal(func_varags_replace_all(None, x="a", y="b", w="x", label="text", data=data), - "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal( + func_varags_replace_all(None, "a", "b", w="x", label="", data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal( + func_varags_replace_all(None, "a", "b", w="x", label="text", + data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + assert_equal( + func_varags_replace_all(None, x="a", y="b", w="x", label="", + data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") + assert_equal( + func_varags_replace_all(None, x="a", y="b", w="x", label="text", + data=data), + "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") try: from pandas.util.testing import assert_produces_warning @@ -246,7 +264,8 @@ def test_no_label_replacements(): @unpack_labeled_data(replace_names=["x", "y"], label_namer=None) def func_no_label(ax, x, y, ls="x", label=None, w="xyz"): - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x), list(y), ls, w, label) + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % ( + list(x), list(y), ls, w, label) data = {"a": [1, 2], "b": [8, 9], "w": "NOT"} assert_equal(func_no_label(None, "a", "b", data=data), @@ -284,18 +303,23 @@ def funcy(ax, *args, **kwargs): if k in kwargs: all_args[i] = kwargs[k] x, y, ls, label, w = all_args - return "x: %s, y: %s, ls: %s, w: %s, label: %s" % (list(x), list(y), ls, w, label) + return "x: %s, y: %s, ls: %s, w: %s, label: %s" % ( + list(x), list(y), ls, w, label) - func = unpack_labeled_data(replace_all_args=True, replace_names=["w"])(funcy) + func = unpack_labeled_data(replace_all_args=True, replace_names=["w"])( + funcy) - # assert_equal(func(None, "a","b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") + # assert_equal(func(None, "a","b", w="x", data=data), + # "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") assert_equal(func(None, "a", "b", w="x", label="", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") assert_equal(func(None, "a", "b", w="x", label="text", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") func2 = unpack_labeled_data(replace_all_args=True, replace_names=["w"], - positional_parameter_names=["x", "y", "ls", "label", "w"])(funcy) + positional_parameter_names=["x", "y", "ls", + "label", "w"])( + funcy) assert_equal(func2(None, "a", "b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: b") From 85d0e7431252ea49575d391893cb1c54c6e0242f Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 6 Aug 2015 23:13:56 -0400 Subject: [PATCH 22/60] ENH: extract label from input data in `plot` If the input data has a `name` attribute and a label is in explicitly passed in, use the name as the label that will be used in automatic calls to `legend`. --- lib/matplotlib/axes/_base.py | 5 ++++- lib/matplotlib/cbook.py | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 8305f010e215..bb0f0850e1fa 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -15,7 +15,7 @@ import matplotlib from matplotlib import cbook -from matplotlib.cbook import _string_to_bool +from matplotlib.cbook import _string_to_bool, iterable, get_index_y, get_label from matplotlib import docstring import matplotlib.colors as mcolors import matplotlib.lines as mlines @@ -349,6 +349,9 @@ def _plot_args(self, tup, kwargs): if v is not None: kw[k] = v + if 'label' not in kwargs or kwargs['label'] is None: + kwargs['label'] = get_label(tup[-1]) + if len(tup) == 2: x = np.atleast_1d(tup[0]) y = np.atleast_1d(tup[-1]) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index ed460315b1c1..2020494c6ba5 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2515,6 +2515,13 @@ def get_index_y(y): y = np.atleast_1d(y) return np.arange(y.shape[0], dtype=float), y + +def get_label(y): + try: + return y.name + except AttributeError: + return None + # Numpy > 1.6.x deprecates putmask in favor of the new copyto. # So long as we support versions 1.6.x and less, we need the # following local version of putmask. We choose to make a From 1f36892d582d1e5b902b741c53875f13a184b7fc Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Sat, 8 Aug 2015 20:07:40 +0200 Subject: [PATCH 23/60] MNT: add a comment about label_naming in plot(...) --- lib/matplotlib/axes/_axes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 04a70e1da3c0..174bfd04e98b 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -1246,6 +1246,7 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, return colls #### Basic plotting + # The label_naming happens in `matplotlib.axes._base._plot_args` @unpack_labeled_data(replace_all_args=True, label_namer=None) @docstring.dedent_interpd def plot(self, *args, **kwargs): From 8dbb2cc4e7c065fc1302b91774bc13705e4eea7f Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Sun, 9 Aug 2015 13:40:36 +0200 Subject: [PATCH 24/60] ENH: Append docs in @unpack_labeled_data --- lib/matplotlib/__init__.py | 32 +++++++++++- .../tests/test_labled_data_unpacking.py | 49 ++++++++++++++++++- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index a416c88b793e..2fc4b8918bb7 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -119,7 +119,7 @@ import functools # cbook must import matplotlib only within function # definitions, so it is safe to import from it here. -from matplotlib.cbook import is_string_like, mplDeprecation +from matplotlib.cbook import is_string_like, mplDeprecation, dedent from matplotlib.compat import subprocess from matplotlib.rcsetup import (defaultParams, validate_backend, @@ -1534,6 +1534,19 @@ def _replacer(data, key): return key +_DATA_DOC_APPENDIX = """ + +Notes +----- + +In addition to the above described arguments, this function can take a +**data** keyword argument. If such a **data** argument is given, the +following arguments are replaced by **data[]**: + +{replaced} +""" + + def unpack_labeled_data(replace_names=None, replace_all_args=False, label_namer=None, positional_parameter_names=None): """ @@ -1708,10 +1721,25 @@ def inner(ax, *args, **kwargs): RuntimeWarning, stacklevel=2) # raise Exception() return func(ax, *args, **kwargs) + pre_doc = inner.__doc__ + if pre_doc is None: + pre_doc = '' + else: + pre_doc = dedent(pre_doc) + _repl = "" + if replace_names is None: + _repl = "* All positional and all keyword arguments." + else: + if len(replace_names) != 0: + _repl = "* All arguments with the following names: '{names}'." + if replace_all_args: + _repl += "\n* All positional arguments." + _repl = _repl.format(names="', '".join(replace_names)) + inner.__doc__ = (pre_doc + + _DATA_DOC_APPENDIX.format(replaced=_repl)) return inner return param - verbose.report('matplotlib version %s' % __version__) verbose.report('verbose.level %s' % verbose.level) verbose.report('interactive is %s' % is_interactive()) diff --git a/lib/matplotlib/tests/test_labled_data_unpacking.py b/lib/matplotlib/tests/test_labled_data_unpacking.py index 68d346b3b859..9629f26a24aa 100644 --- a/lib/matplotlib/tests/test_labled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labled_data_unpacking.py @@ -1,7 +1,8 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) -from nose.tools import assert_raises, assert_equal +from nose.tools import (assert_raises, assert_equal, assert_regexp_matches, + assert_not_regexp_matches) from nose.plugins.skip import SkipTest from .. import unpack_labeled_data @@ -327,3 +328,49 @@ def funcy(ax, *args, **kwargs): "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") assert_equal(func2(None, "a", "b", w="x", label="text", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") + + +def test_docstring_addition(): + @unpack_labeled_data() + def funcy(ax, *args, **kwargs): + """Funcy does nothing""" + pass + + assert_regexp_matches(funcy.__doc__, + r".*All positional and all keyword arguments\.") + assert_not_regexp_matches(funcy.__doc__, r".*All positional arguments\.") + assert_not_regexp_matches(funcy.__doc__, + r".*All arguments with the following names: .*") + + @unpack_labeled_data(replace_all_args=True, replace_names=[]) + def funcy(ax, x, y, z, bar=None): + """Funcy does nothing""" + pass + + assert_regexp_matches(funcy.__doc__, r".*All positional arguments\.") + assert_not_regexp_matches(funcy.__doc__, + r".*All positional and all keyword arguments\.") + assert_not_regexp_matches(funcy.__doc__, + r".*All arguments with the following names: .*") + + @unpack_labeled_data(replace_all_args=True, replace_names=["bar"]) + def funcy(ax, x, y, z, bar=None): + """Funcy does nothing""" + pass + + assert_regexp_matches(funcy.__doc__, r".*All positional arguments\.") + assert_regexp_matches(funcy.__doc__, + r".*All arguments with the following names: 'bar'\.") + assert_not_regexp_matches(funcy.__doc__, + r".*All positional and all keyword arguments\.") + + @unpack_labeled_data(replace_names=["x", "bar"]) + def funcy(ax, x, y, z, bar=None): + """Funcy does nothing""" + pass + + assert_regexp_matches(funcy.__doc__, + r".*All arguments with the following names: 'x', 'bar'\.") + assert_not_regexp_matches(funcy.__doc__, + r".*All positional and all keyword arguments\.") + assert_not_regexp_matches(funcy.__doc__, r".*All positional arguments\.") From c768f0d69f670578ff52666b582fde4c6f85edbd Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Sun, 9 Aug 2015 22:33:08 +0200 Subject: [PATCH 25/60] ENH: accept a function for arg names in @unpack_labeled_data Now variable length *args can be named and individually replaced. This unfortunately does not help for plot() because some abiguity as there is no way to know in some cases if a users wants to get something replaced or not: ```python data = {"x":X,"y":Y, "c":C} plot(x,y,c,x,y,c, data=data) plot(x,y,x,y,x,y, data=data) ``` Here the first call will get all args replaced and matplotlib would then see all 6 arrays which would translate into three lines with the same color information (like the second call). In some cases this can be catched (like for one/three lines with 'x,y,c' combos -> 3/9 args, will end up with one "missing" if all are treated as replaceable) but in some cases it's not possible without guesswork. :-( But in other settings, where the number of args determines the order/names, this works. Also activate the tests for the first time... --- lib/matplotlib/__init__.py | 85 +++++++--- .../tests/test_labled_data_unpacking.py | 145 ++++++++++++++++-- 2 files changed, 190 insertions(+), 40 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 2fc4b8918bb7..063f7abdbc19 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1458,6 +1458,7 @@ def tk_window_focus(): 'matplotlib.tests.test_triangulation', 'matplotlib.tests.test_widgets', 'matplotlib.tests.test_cycles', + 'matplotlib.tests.test_labeled_data_unpacking', 'matplotlib.sphinxext.tests.test_tinypages', 'mpl_toolkits.tests.test_mplot3d', 'mpl_toolkits.tests.test_axes_grid1', @@ -1566,18 +1567,21 @@ def foo(ax, *args, **kwargs) replace_all_args : bool, default: False If True, all arguments in *args get replaced, even if they are not in replace_names. - NOTE: this should be used only when the order of the names depends on - the number of *args. label_namer : string, optional, default: None The name of the parameter which argument should be used as label, if label is not set. If None, the label keyword argument is not set. - positional_parameter_names : list of strings, optional, default: None + positional_parameter_names : list of strings or callable, optional The full list of positional parameter names (excluding an explicit `ax`/'self' argument at the first place and including all possible positional parameter in `*args`), in the right order. Can also include all other keyword parameter. Only needed if the wrapped function does contain `*args` and (replace_names is not None or replace_all_args is - False). + False). If it is a callable, it will be called with the actual + tuple of *args and the data and should return a list like + above. + NOTE: callables should only be used when the names and order of *args + can only be determined at runtime. Please use list of names + when the order and names of *args is clear before runtime! """ if replace_names is not None: replace_names = set(replace_names) @@ -1590,16 +1594,19 @@ def param(func): _has_no_varargs = arg_spec.varargs is None _has_varkwargs = arg_spec.keywords is not None + # Import-time check: do we have enough information to replace *args? + arg_names_at_runtime = False # there can't be any positional arguments behind *args and no - # positional args can end up in **kwargs, so we only need to check for - # varargs: + # positional args can end up in **kwargs, so only *varargs make + # problems. # http://stupidpythonideas.blogspot.de/2013/08/arguments-and-parameters.html if _has_no_varargs: + # all args are "named", so no problem # remove the first "ax" / self arg arg_names = _arg_names[1:] else: - # in this case we need a supplied list of arguments or we need to - # replace all variables -> compile time check + # Here we have "unnamed" variables and we need a way to determine + # whether to replace a arg or not if replace_names is None: # all argnames should be replaced arg_names = None @@ -1618,7 +1625,13 @@ def param(func): raise AssertionError(msg % func.__name__) else: if positional_parameter_names is not None: - arg_names = positional_parameter_names + if callable(positional_parameter_names): + # determined by the function at runtime + arg_names_at_runtime = True + # so that we don't compute the label_pos at import time + arg_names = [] + else: + arg_names = positional_parameter_names else: if replace_all_args: arg_names = [] @@ -1633,7 +1646,9 @@ def param(func): # arguments label_pos = 9999 # bigger than all "possible" argument lists label_namer_pos = 9999 # bigger than all "possible" argument lists - if label_namer and arg_names and (label_namer in arg_names): + if (label_namer # we actually want a label here ... + and arg_names # and we can determine a label in *args ... + and (label_namer in arg_names)): # and it is in *args label_namer_pos = arg_names.index(label_namer) if "label" in arg_names: label_pos = arg_names.index("label") @@ -1642,7 +1657,7 @@ def param(func): # arg_names... Unfortunately the label_namer can be in **kwargs, # which we can't detect here and which results in a non-set label # which might surprise the user :-( - if label_namer and not _has_varkwargs: + if label_namer and not arg_names_at_runtime and not _has_varkwargs: if not arg_names: msg = ("label_namer '%s' can't be found as the parameter " "without 'positional_parameter_names'.") @@ -1657,53 +1672,74 @@ def param(func): @functools.wraps(func) def inner(ax, *args, **kwargs): - data = kwargs.pop('data', None) + # this is needed because we want to change these values if + # arg_names_at_runtime==True, but python does not allow assigning + # to a variable in a outer scope. So use some new local ones and + # set them to the already computed values. + _label_pos = label_pos + _label_namer_pos = label_namer_pos + _arg_names = arg_names + label = None + + data = kwargs.pop('data', None) if data is not None: + if arg_names_at_runtime: + # update the information about replace names and + # label position + _arg_names = positional_parameter_names(args, data) + if (label_namer # we actually want a label here ... + and _arg_names # and we can find a label in *args ... + and (label_namer in _arg_names)): # and it is in *args + _label_namer_pos = _arg_names.index(label_namer) + if "label" in _arg_names: + _label_pos = arg_names.index("label") + # save the current label_namer value so that it can be used as # a label - if label_namer_pos < len(args): - label = args[label_namer_pos] + if _label_namer_pos < len(args): + label = args[_label_namer_pos] else: label = kwargs.get(label_namer, None) # ensure a string, as label can't be anything else if not isinstance(label, six.string_types): label = None - if (replace_names is None) or replace_all_args: + if (replace_names is None) or (replace_all_args is True): # all should be replaced args = tuple(_replacer(data, a) for j, a in enumerate(args)) else: - # An arg is replaced if the arg_name of that position is in - # replace_names ... - if len(arg_names) < len(args): + # An arg is replaced if the arg_name of that position is + # in replace_names ... + if len(_arg_names) < len(args): raise RuntimeError( "Got more args than function expects") args = tuple(_replacer(data, a) - if arg_names[j] in replace_names else a + if _arg_names[j] in replace_names else a for j, a in enumerate(args)) if replace_names is None: + # replace all kwargs ... kwargs = dict((k, _replacer(data, v)) for k, v in six.iteritems(kwargs)) else: - # ... or a kwarg of that name in replace_names + # ... or only if a kwarg of that name is in replace_names kwargs = dict((k, _replacer(data, v) if k in replace_names else v) for k, v in six.iteritems(kwargs)) # replace the label if this func "wants" a label arg and the user - # didn't set one Note: if the usere puts in "label=None", it does + # didn't set one. Note: if the user puts in "label=None", it does # *NOT* get replaced! user_supplied_label = ( - (len(args) >= label_pos) or # label is included in args + (len(args) >= _label_pos) or # label is included in args ('label' in kwargs) # ... or in kwargs ) if (label_namer and not user_supplied_label): - if label_namer_pos < len(args): + if _label_namer_pos < len(args): try: - kwargs['label'] = args[label_namer_pos].name + kwargs['label'] = args[_label_namer_pos].name except AttributeError: kwargs['label'] = label elif label_namer in kwargs: @@ -1740,6 +1776,7 @@ def inner(ax, *args, **kwargs): return inner return param + verbose.report('matplotlib version %s' % __version__) verbose.report('verbose.level %s' % verbose.level) verbose.report('interactive is %s' % is_interactive()) diff --git a/lib/matplotlib/tests/test_labled_data_unpacking.py b/lib/matplotlib/tests/test_labled_data_unpacking.py index 9629f26a24aa..648ad5f861fa 100644 --- a/lib/matplotlib/tests/test_labled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labled_data_unpacking.py @@ -5,17 +5,87 @@ assert_not_regexp_matches) from nose.plugins.skip import SkipTest +from matplotlib.cbook import is_string_like, iterable + +from contextlib import contextmanager + from .. import unpack_labeled_data +def is_list_like(obj): + return not is_string_like(obj) and iterable(obj) + + +# stolen from pandas +@contextmanager +def assert_produces_warning(expected_warning=Warning, filter_level="always", + clear=None): + """ + Context manager for running code that expects to raise (or not raise) + warnings. Checks that code raises the expected warning and only the + expected warning. Pass ``False`` or ``None`` to check that it does *not* + raise a warning. Defaults to ``exception.Warning``, baseclass of all + Warnings. (basically a wrapper around ``warnings.catch_warnings``). + + >>> import warnings + >>> with assert_produces_warning(): + ... warnings.warn(UserWarning()) + ... + >>> with assert_produces_warning(False): + ... warnings.warn(RuntimeWarning()) + ... + Traceback (most recent call last): + ... + AssertionError: Caused unexpected warning(s): ['RuntimeWarning']. + >>> with assert_produces_warning(UserWarning): + ... warnings.warn(RuntimeWarning()) + Traceback (most recent call last): + ... + AssertionError: Did not see expected warning of class 'UserWarning'. + + ..warn:: This is *not* thread-safe. + """ + import warnings + + with warnings.catch_warnings(record=True) as w: + + if clear is not None: + # make sure that we are clearning these warnings + # if they have happened before + # to guarantee that we will catch them + if not is_list_like(clear): + clear = [clear] + for m in clear: + try: + m.__warningregistry__.clear() + except: + pass + + saw_warning = False + warnings.simplefilter(filter_level) + yield w + extra_warnings = [] + for actual_warning in w: + if (expected_warning and issubclass(actual_warning.category, + expected_warning)): + saw_warning = True + else: + extra_warnings.append(actual_warning.category.__name__) + if expected_warning: + assert saw_warning, ("Did not see expected warning of class %r." + % expected_warning.__name__) + assert not extra_warnings, ("Caused unexpected warning(s): %r." + % extra_warnings) + + # these two get used in multiple tests, so define them here -@unpack_labeled_data(replace_names=["x", "y"]) +@unpack_labeled_data(replace_names=["x", "y"], label_namer="y") def plot_func(ax, x, y, ls="x", label=None, w="xyz"): return ("x: %s, y: %s, ls: %s, w: %s, label: %s" % ( list(x), list(y), ls, w, label)) -@unpack_labeled_data(replace_names=["x", "y"], +@unpack_labeled_data(replace_names=["x", "y"], label_namer="y", positional_parameter_names=["x", "y", "ls", "label", "w"]) def plot_func_varags(ax, *args, **kwargs): all_args = [None, None, "x", None, "xyz"] @@ -84,10 +154,6 @@ def f(): def test_label_problems_at_runtime(): """Tests for behaviour which would actually be nice to get rid of.""" - try: - from pandas.util.testing import assert_produces_warning - except ImportError: - raise SkipTest("Pandas not installed") @unpack_labeled_data(label_namer="z") def func(*args, **kwargs): @@ -198,7 +264,7 @@ def test_function_call_replace_all(): """Test without a "replace_names" argument, all vars should be replaced""" data = {"a": [1, 2], "b": [8, 9], "x": "xyz"} - @unpack_labeled_data() + @unpack_labeled_data(label_namer="y") def func_replace_all(ax, x, y, ls="x", label=None, w="NOT"): return "x: %s, y: %s, ls: %s, w: %s, label: %s" % ( list(x), list(y), ls, w, label) @@ -219,7 +285,7 @@ def func_replace_all(ax, x, y, ls="x", label=None, w="NOT"): func_replace_all(None, x="a", y="b", w="x", label="text", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") - @unpack_labeled_data() + @unpack_labeled_data(label_namer="y") def func_varags_replace_all(ax, *args, **kwargs): all_args = [None, None, "x", None, "xyz"] for i, v in enumerate(args): @@ -251,13 +317,9 @@ def func_varags_replace_all(ax, *args, **kwargs): data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") - try: - from pandas.util.testing import assert_produces_warning - + with assert_produces_warning(): assert_equal(func_varags_replace_all(None, "a", "b", w="x", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") - except ImportError: - pass def test_no_label_replacements(): @@ -280,7 +342,7 @@ def func_no_label(ax, x, y, ls="x", label=None, w="xyz"): def test_more_args_than_pos_parameter(): - @unpack_labeled_data(replace_names=["x", "y"]) + @unpack_labeled_data(replace_names=["x", "y"], label_namer="y") def func(ax, x, y, z=1): pass @@ -307,7 +369,8 @@ def funcy(ax, *args, **kwargs): return "x: %s, y: %s, ls: %s, w: %s, label: %s" % ( list(x), list(y), ls, w, label) - func = unpack_labeled_data(replace_all_args=True, replace_names=["w"])( + func = unpack_labeled_data(replace_all_args=True, replace_names=["w"], + label_namer="y")( funcy) # assert_equal(func(None, "a","b", w="x", data=data), @@ -318,6 +381,7 @@ def funcy(ax, *args, **kwargs): "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: text") func2 = unpack_labeled_data(replace_all_args=True, replace_names=["w"], + label_namer="y", positional_parameter_names=["x", "y", "ls", "label", "w"])( funcy) @@ -370,7 +434,56 @@ def funcy(ax, x, y, z, bar=None): pass assert_regexp_matches(funcy.__doc__, - r".*All arguments with the following names: 'x', 'bar'\.") + r".*All arguments with the following names: 'x', 'bar'\.") assert_not_regexp_matches(funcy.__doc__, r".*All positional and all keyword arguments\.") assert_not_regexp_matches(funcy.__doc__, r".*All positional arguments\.") + + +def test_positional_parameter_names_as_function(): + # + # this is a replace for plot, which can take args as x,y,c,x,y,c or x,y,x,y,c,x,y + def replacer(args, data): + _replacer = [] + remaining = args + while 1: + if len(remaining) == 1: + import warnings + + msg = "Missing argument: this can happen if a color spec ('c') is in data" + warnings.warn(msg, RuntimeWarning, stacklevel=3) + _replacer += ["x"] + elif len(remaining) == 2: + _replacer += ["x", "y"] + elif len(remaining) == 3: + if remaining[2] in data: + import warnings + + msg = "Found a color spec ('c') in data." + warnings.warn(msg, RuntimeWarning, stacklevel=3) + _replacer += ["x", "y", "c"] + + if len(remaining) <= 3: + return _replacer + + # More than 3 -> split off the beginning and continue + if remaining[2] not in data: + _replacer += ["x", "y", "c"] + isplit = 3 + else: + _replacer += ["x", "y"] + isplit = 2 + remaining = remaining[isplit:] + + @unpack_labeled_data(replace_names=["x", "y"], + positional_parameter_names=replacer) + def funcy(ax, *args, **kwargs): + return "{args} | {kwargs}".format(args=args, kwargs=kwargs) + + data = {"x": "X", "y": "Y"} + assert_equal(funcy(None, "x", "y", "c", "x", "y", "x", "y", data=data), + "('X', 'Y', 'c', 'X', 'Y', 'X', 'Y') | {}") + data = {"x": "X", "y": "Y", "c": "!!"} + with assert_produces_warning(RuntimeWarning): + assert_equal(funcy(None, "x", "y", "c", "x", "y", "x", "y", data=data), + "('X', 'Y', '!!', 'X', 'Y', 'X', 'y') | {}") From 385b8e339ae22f991c5439440ab119a5d68a6d13 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Sun, 9 Aug 2015 23:11:01 +0200 Subject: [PATCH 26/60] ENH: use the new 'names via function' for @unpack_labeled_data --- lib/matplotlib/axes/_axes.py | 42 +++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 174bfd04e98b..4b07247043aa 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -1245,9 +1245,43 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, return colls + def _plot_args_replacer(self, args, data): + _replacer = [] + remaining = args + while 1: + if len(remaining) == 1: + import warnings + + msg = "Missing argument: this can happen if a color spec ('c') is in data" + warnings.warn(msg, RuntimeWarning, stacklevel=3) + _replacer += ["x"] + elif len(remaining) == 2: + _replacer += ["x", "y"] + elif len(remaining) == 3: + if remaining[2] in data: + import warnings + + msg = "Found a color spec ('c') in data." + warnings.warn(msg, RuntimeWarning, stacklevel=3) + _replacer += ["x", "y", "c"] + + if len(remaining) <= 3: + return _replacer + + # More than 3 -> split off the beginning and continue + if remaining[2] not in data: + _replacer += ["x", "y", "c"] + isplit = 3 + else: + _replacer += ["x", "y"] + isplit = 2 + remaining = remaining[isplit:] + #### Basic plotting # The label_naming happens in `matplotlib.axes._base._plot_args` - @unpack_labeled_data(replace_all_args=True, label_namer=None) + @unpack_labeled_data(replace_names=["x", "y"], + positional_parameter_names=_plot_args_replacer, + label_namer=None) @docstring.dedent_interpd def plot(self, *args, **kwargs): """ @@ -4378,6 +4412,7 @@ def quiverkey(self, *args, **kw): return qk quiverkey.__doc__ = mquiver.QuiverKey.quiverkey_doc + # args can by a combination if X, Y, U, V, C and all should be replaced @unpack_labeled_data(replace_all_args=True, label_namer=None) def quiver(self, *args, **kw): if not self._hold: @@ -4389,6 +4424,7 @@ def quiver(self, *args, **kw): return q quiver.__doc__ = mquiver.Quiver.quiver_doc + # args can by either Y or y1,y2,... and all should be replaced @unpack_labeled_data(replace_all_args=True, label_namer=None) def stackplot(self, x, *args, **kwargs): return mstack.stackplot(self, x, *args, **kwargs) @@ -4416,6 +4452,7 @@ def streamplot(self, x, y, u, v, density=1, linewidth=None, color=None, return stream_container streamplot.__doc__ = mstream.streamplot.__doc__ + # args can be some combination of X, Y, U, V, C and all should be replaced @unpack_labeled_data(replace_all_args=True, label_namer=None) @docstring.dedent_interpd def barbs(self, *args, **kw): @@ -7155,7 +7192,7 @@ def matshow(self, Z, **kwargs): integer=True)) return im - @unpack_labeled_data(replace_all_args=True, label_namer=None) + @unpack_labeled_data(replace_names=["dataset"], label_namer=None) def violinplot(self, dataset, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False, points=100, bw_method=None): @@ -7260,7 +7297,6 @@ def _kde_method(X, coords): widths=widths, showmeans=showmeans, showextrema=showextrema, showmedians=showmedians) - @unpack_labeled_data(replace_all_args=True, label_namer=None) def violin(self, vpstats, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False): """Drawing function for violin plots. From 812f74fef28076e8f037abd45970df27555502e7 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Sun, 9 Aug 2015 23:14:04 +0200 Subject: [PATCH 27/60] FIX: rename test file for @unpack_labeled_data --- ...st_labled_data_unpacking.py => test_labeled_data_unpacking.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/matplotlib/tests/{test_labled_data_unpacking.py => test_labeled_data_unpacking.py} (100%) diff --git a/lib/matplotlib/tests/test_labled_data_unpacking.py b/lib/matplotlib/tests/test_labeled_data_unpacking.py similarity index 100% rename from lib/matplotlib/tests/test_labled_data_unpacking.py rename to lib/matplotlib/tests/test_labeled_data_unpacking.py From 2194e80fb82d24880ea5ae1ac5fa81d188ba6f93 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Thu, 13 Aug 2015 17:07:29 +0200 Subject: [PATCH 28/60] FIX: assert_regexp_matches is only in py2.7 --- .../tests/test_labeled_data_unpacking.py | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/lib/matplotlib/tests/test_labeled_data_unpacking.py b/lib/matplotlib/tests/test_labeled_data_unpacking.py index 648ad5f861fa..882bd7e68c11 100644 --- a/lib/matplotlib/tests/test_labeled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labeled_data_unpacking.py @@ -1,10 +1,26 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) -from nose.tools import (assert_raises, assert_equal, assert_regexp_matches, - assert_not_regexp_matches) +from nose.tools import (assert_raises, assert_equal) from nose.plugins.skip import SkipTest +try: + # 3.2+ versions + from nose.tools import assert_regex, assert_not_regex +except ImportError: + try: + # 2.7 versions + from nose.tools import assert_regexp_matches, assert_not_regexp_matches + assert_regex = assert_regexp_matches + assert_not_regex = assert_not_regexp_matches + except ImportError: + # 2.6 versions + def noop(txt, regex): + raise SkipTest("No assert for regex matching in py2.6") + assert_regex = noop + assert_not_regex = noop + + from matplotlib.cbook import is_string_like, iterable from contextlib import contextmanager @@ -400,10 +416,10 @@ def funcy(ax, *args, **kwargs): """Funcy does nothing""" pass - assert_regexp_matches(funcy.__doc__, + assert_regex(funcy.__doc__, r".*All positional and all keyword arguments\.") - assert_not_regexp_matches(funcy.__doc__, r".*All positional arguments\.") - assert_not_regexp_matches(funcy.__doc__, + assert_not_regex(funcy.__doc__, r".*All positional arguments\.") + assert_not_regex(funcy.__doc__, r".*All arguments with the following names: .*") @unpack_labeled_data(replace_all_args=True, replace_names=[]) @@ -411,10 +427,10 @@ def funcy(ax, x, y, z, bar=None): """Funcy does nothing""" pass - assert_regexp_matches(funcy.__doc__, r".*All positional arguments\.") - assert_not_regexp_matches(funcy.__doc__, + assert_regex(funcy.__doc__, r".*All positional arguments\.") + assert_not_regex(funcy.__doc__, r".*All positional and all keyword arguments\.") - assert_not_regexp_matches(funcy.__doc__, + assert_not_regex(funcy.__doc__, r".*All arguments with the following names: .*") @unpack_labeled_data(replace_all_args=True, replace_names=["bar"]) @@ -422,10 +438,10 @@ def funcy(ax, x, y, z, bar=None): """Funcy does nothing""" pass - assert_regexp_matches(funcy.__doc__, r".*All positional arguments\.") - assert_regexp_matches(funcy.__doc__, + assert_regex(funcy.__doc__, r".*All positional arguments\.") + assert_regex(funcy.__doc__, r".*All arguments with the following names: 'bar'\.") - assert_not_regexp_matches(funcy.__doc__, + assert_not_regex(funcy.__doc__, r".*All positional and all keyword arguments\.") @unpack_labeled_data(replace_names=["x", "bar"]) @@ -433,11 +449,11 @@ def funcy(ax, x, y, z, bar=None): """Funcy does nothing""" pass - assert_regexp_matches(funcy.__doc__, + assert_regex(funcy.__doc__, r".*All arguments with the following names: 'x', 'bar'\.") - assert_not_regexp_matches(funcy.__doc__, + assert_not_regex(funcy.__doc__, r".*All positional and all keyword arguments\.") - assert_not_regexp_matches(funcy.__doc__, r".*All positional arguments\.") + assert_not_regex(funcy.__doc__, r".*All positional arguments\.") def test_positional_parameter_names_as_function(): From 52d23c9a46761a77c71dafaa562b2c3bb685f61c Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Thu, 13 Aug 2015 17:10:37 +0200 Subject: [PATCH 29/60] TST: workaround for unicode prefix in test matching --- lib/matplotlib/tests/test_labeled_data_unpacking.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_labeled_data_unpacking.py b/lib/matplotlib/tests/test_labeled_data_unpacking.py index 882bd7e68c11..c861033e315e 100644 --- a/lib/matplotlib/tests/test_labeled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labeled_data_unpacking.py @@ -1,5 +1,4 @@ -from __future__ import (absolute_import, division, print_function, - unicode_literals) +from __future__ import (absolute_import, division, print_function) from nose.tools import (assert_raises, assert_equal) from nose.plugins.skip import SkipTest From 8aa800be4df7984a58c9e8dcd20658233f8e3663 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Thu, 13 Aug 2015 17:17:06 +0200 Subject: [PATCH 30/60] PEP8: line wrapping, spacing --- lib/matplotlib/__init__.py | 12 ++++++------ lib/matplotlib/axes/_axes.py | 3 ++- lib/matplotlib/tests/test_labeled_data_unpacking.py | 7 ++++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 063f7abdbc19..e7cf84272b53 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1646,9 +1646,9 @@ def param(func): # arguments label_pos = 9999 # bigger than all "possible" argument lists label_namer_pos = 9999 # bigger than all "possible" argument lists - if (label_namer # we actually want a label here ... - and arg_names # and we can determine a label in *args ... - and (label_namer in arg_names)): # and it is in *args + if (label_namer # we actually want a label here ... + and arg_names # and we can determine a label in *args ... + and (label_namer in arg_names)): # and it is in *args label_namer_pos = arg_names.index(label_namer) if "label" in arg_names: label_pos = arg_names.index("label") @@ -1688,9 +1688,9 @@ def inner(ax, *args, **kwargs): # update the information about replace names and # label position _arg_names = positional_parameter_names(args, data) - if (label_namer # we actually want a label here ... - and _arg_names # and we can find a label in *args ... - and (label_namer in _arg_names)): # and it is in *args + if (label_namer # we actually want a label here ... + and _arg_names # and we can find a label in *args ... + and (label_namer in _arg_names)): # and it is in *args _label_namer_pos = _arg_names.index(label_namer) if "label" in _arg_names: _label_pos = arg_names.index("label") diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 4b07247043aa..8f9f3cf4f77c 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -1252,7 +1252,8 @@ def _plot_args_replacer(self, args, data): if len(remaining) == 1: import warnings - msg = "Missing argument: this can happen if a color spec ('c') is in data" + msg = "Missing argument: this can happen if a color spec " \ + "('c') is in data" warnings.warn(msg, RuntimeWarning, stacklevel=3) _replacer += ["x"] elif len(remaining) == 2: diff --git a/lib/matplotlib/tests/test_labeled_data_unpacking.py b/lib/matplotlib/tests/test_labeled_data_unpacking.py index c861033e315e..eb303c3d2e47 100644 --- a/lib/matplotlib/tests/test_labeled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labeled_data_unpacking.py @@ -449,7 +449,7 @@ def funcy(ax, x, y, z, bar=None): pass assert_regex(funcy.__doc__, - r".*All arguments with the following names: 'x', 'bar'\.") + r".*All arguments with the following names: 'x', 'bar'\.") assert_not_regex(funcy.__doc__, r".*All positional and all keyword arguments\.") assert_not_regex(funcy.__doc__, r".*All positional arguments\.") @@ -457,7 +457,8 @@ def funcy(ax, x, y, z, bar=None): def test_positional_parameter_names_as_function(): # - # this is a replace for plot, which can take args as x,y,c,x,y,c or x,y,x,y,c,x,y + # this is a replace for plot, which can take args as + # x,y,c,x,y,c or x,y,x,y,c,x,y def replacer(args, data): _replacer = [] remaining = args @@ -465,7 +466,7 @@ def replacer(args, data): if len(remaining) == 1: import warnings - msg = "Missing argument: this can happen if a color spec ('c') is in data" + msg = "Missing argument: color spec ('c=..') is in data?" warnings.warn(msg, RuntimeWarning, stacklevel=3) _replacer += ["x"] elif len(remaining) == 2: From ecc91e591f9327d58dac85eefe150ea4b719cfe5 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Thu, 13 Aug 2015 17:23:28 +0200 Subject: [PATCH 31/60] TST: workaround for unpredictable order in list printing --- lib/matplotlib/tests/test_labeled_data_unpacking.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_labeled_data_unpacking.py b/lib/matplotlib/tests/test_labeled_data_unpacking.py index eb303c3d2e47..e798d7bcfc96 100644 --- a/lib/matplotlib/tests/test_labeled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labeled_data_unpacking.py @@ -448,8 +448,11 @@ def funcy(ax, x, y, z, bar=None): """Funcy does nothing""" pass + # lists can print in any order, so test for both x,bar and bar,x assert_regex(funcy.__doc__, - r".*All arguments with the following names: 'x', 'bar'\.") + r".*All arguments with the following names: '.*', '.*'\.") + assert_regex(funcy.__doc__, r".*'x'.*") + assert_regex(funcy.__doc__, r".*'bar'.*") assert_not_regex(funcy.__doc__, r".*All positional and all keyword arguments\.") assert_not_regex(funcy.__doc__, r".*All positional arguments\.") From b09370ede758f62284ccce33f4bd15955434f351 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 20 Aug 2015 07:32:34 -0400 Subject: [PATCH 32/60] WIP: review work should split this up into smaller commits --- lib/matplotlib/__init__.py | 24 +++++-------- lib/matplotlib/axes/_axes.py | 69 ++++++++++++++++++------------------ lib/matplotlib/axes/_base.py | 2 +- lib/matplotlib/cbook.py | 4 +-- 4 files changed, 47 insertions(+), 52 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index e7cf84272b53..7997b35ede83 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -119,7 +119,7 @@ import functools # cbook must import matplotlib only within function # definitions, so it is safe to import from it here. -from matplotlib.cbook import is_string_like, mplDeprecation, dedent +from matplotlib.cbook import is_string_like, mplDeprecation, dedent, get_label from matplotlib.compat import subprocess from matplotlib.rcsetup import (defaultParams, validate_backend, @@ -1646,9 +1646,9 @@ def param(func): # arguments label_pos = 9999 # bigger than all "possible" argument lists label_namer_pos = 9999 # bigger than all "possible" argument lists - if (label_namer # we actually want a label here ... - and arg_names # and we can determine a label in *args ... - and (label_namer in arg_names)): # and it is in *args + if (label_namer and # we actually want a label here ... + arg_names and # and we can determine a label in *args ... + (label_namer in arg_names)): # and it is in *args label_namer_pos = arg_names.index(label_namer) if "label" in arg_names: label_pos = arg_names.index("label") @@ -1688,9 +1688,9 @@ def inner(ax, *args, **kwargs): # update the information about replace names and # label position _arg_names = positional_parameter_names(args, data) - if (label_namer # we actually want a label here ... - and _arg_names # and we can find a label in *args ... - and (label_namer in _arg_names)): # and it is in *args + if (label_namer and # we actually want a label here ... + _arg_names and # and we can find a label in *args + (label_namer in _arg_names)): # and it is in *args _label_namer_pos = _arg_names.index(label_namer) if "label" in _arg_names: _label_pos = arg_names.index("label") @@ -1738,15 +1738,9 @@ def inner(ax, *args, **kwargs): ) if (label_namer and not user_supplied_label): if _label_namer_pos < len(args): - try: - kwargs['label'] = args[_label_namer_pos].name - except AttributeError: - kwargs['label'] = label + kwargs['label'] = get_label(args[_label_namer_pos], label) elif label_namer in kwargs: - try: - kwargs['label'] = kwargs[label_namer].name - except AttributeError: - kwargs['label'] = label + kwargs['label'] = get_label(kwargs[label_namer], label) else: import warnings msg = ("Tried to set a label via parameter '%s' in " diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 8f9f3cf4f77c..c5b7982f4b77 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -40,6 +40,7 @@ from matplotlib.axes._base import _AxesBase from matplotlib.axes._base import _process_plot_format + rcParams = matplotlib.rcParams iterable = cbook.iterable @@ -47,6 +48,39 @@ is_sequence_of_strings = cbook.is_sequence_of_strings +def _plot_args_replacer(args, data): + _replacer = [] + remaining = args + while 1: + if len(remaining) == 1: + + msg = ("Missing argument: this can happen if a color spec " + "('c') is in `data`") + warnings.warn(msg, RuntimeWarning, stacklevel=3) + _replacer += ["x"] + elif len(remaining) == 2: + _replacer += ["x", "y"] + elif len(remaining) == 3: + if remaining[2] in data: + import warnings + + msg = "Found a color spec ('c') in data." + warnings.warn(msg, RuntimeWarning, stacklevel=3) + _replacer += ["x", "y", "c"] + + if len(remaining) <= 3: + return _replacer + + # More than 3 -> split off the beginning and continue + if remaining[2] not in data: + _replacer += ["x", "y", "c"] + isplit = 3 + else: + _replacer += ["x", "y"] + isplit = 2 + remaining = remaining[isplit:] + + # The axes module contains all the wrappers to plotting functions. # All the other methods should go in the _AxesBase class. @@ -1245,40 +1279,7 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, return colls - def _plot_args_replacer(self, args, data): - _replacer = [] - remaining = args - while 1: - if len(remaining) == 1: - import warnings - - msg = "Missing argument: this can happen if a color spec " \ - "('c') is in data" - warnings.warn(msg, RuntimeWarning, stacklevel=3) - _replacer += ["x"] - elif len(remaining) == 2: - _replacer += ["x", "y"] - elif len(remaining) == 3: - if remaining[2] in data: - import warnings - - msg = "Found a color spec ('c') in data." - warnings.warn(msg, RuntimeWarning, stacklevel=3) - _replacer += ["x", "y", "c"] - - if len(remaining) <= 3: - return _replacer - - # More than 3 -> split off the beginning and continue - if remaining[2] not in data: - _replacer += ["x", "y", "c"] - isplit = 3 - else: - _replacer += ["x", "y"] - isplit = 2 - remaining = remaining[isplit:] - - #### Basic plotting + # ### Basic plotting # The label_naming happens in `matplotlib.axes._base._plot_args` @unpack_labeled_data(replace_names=["x", "y"], positional_parameter_names=_plot_args_replacer, diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index bb0f0850e1fa..f7e64aba22ab 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -350,7 +350,7 @@ def _plot_args(self, tup, kwargs): kw[k] = v if 'label' not in kwargs or kwargs['label'] is None: - kwargs['label'] = get_label(tup[-1]) + kwargs['label'] = get_label(tup[-1], None) if len(tup) == 2: x = np.atleast_1d(tup[0]) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 2020494c6ba5..d2409396ce3d 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2516,11 +2516,11 @@ def get_index_y(y): return np.arange(y.shape[0], dtype=float), y -def get_label(y): +def get_label(y, default_name): try: return y.name except AttributeError: - return None + return default_name # Numpy > 1.6.x deprecates putmask in favor of the new copyto. # So long as we support versions 1.6.x and less, we need the From 0f781a16c2aaab7e64241cc1bbd52f349f2c4e2e Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Wed, 26 Aug 2015 10:05:58 +0200 Subject: [PATCH 33/60] TST: test the plot arg replacer directly --- lib/matplotlib/axes/_axes.py | 1 + .../tests/test_labeled_data_unpacking.py | 54 +++++++------------ 2 files changed, 20 insertions(+), 35 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index c5b7982f4b77..626baf7adf36 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -68,6 +68,7 @@ def _plot_args_replacer(args, data): warnings.warn(msg, RuntimeWarning, stacklevel=3) _replacer += ["x", "y", "c"] + # if less than 3, the above code handled it so just return if len(remaining) <= 3: return _replacer diff --git a/lib/matplotlib/tests/test_labeled_data_unpacking.py b/lib/matplotlib/tests/test_labeled_data_unpacking.py index e798d7bcfc96..1f978fb3fefb 100644 --- a/lib/matplotlib/tests/test_labeled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labeled_data_unpacking.py @@ -459,50 +459,34 @@ def funcy(ax, x, y, z, bar=None): def test_positional_parameter_names_as_function(): - # - # this is a replace for plot, which can take args as - # x,y,c,x,y,c or x,y,x,y,c,x,y - def replacer(args, data): - _replacer = [] - remaining = args - while 1: - if len(remaining) == 1: - import warnings - - msg = "Missing argument: color spec ('c=..') is in data?" - warnings.warn(msg, RuntimeWarning, stacklevel=3) - _replacer += ["x"] - elif len(remaining) == 2: - _replacer += ["x", "y"] - elif len(remaining) == 3: - if remaining[2] in data: - import warnings - - msg = "Found a color spec ('c') in data." - warnings.warn(msg, RuntimeWarning, stacklevel=3) - _replacer += ["x", "y", "c"] - - if len(remaining) <= 3: - return _replacer - - # More than 3 -> split off the beginning and continue - if remaining[2] not in data: - _replacer += ["x", "y", "c"] - isplit = 3 - else: - _replacer += ["x", "y"] - isplit = 2 - remaining = remaining[isplit:] + # Also test the _plot_arg_replacer for plot... + from matplotlib.axes._axes import _plot_args_replacer + # this is a replace for plot, which can take args as + # x,y,c,x,y,c or x,y,x,y,c,x,y or any other way... :-/ @unpack_labeled_data(replace_names=["x", "y"], - positional_parameter_names=replacer) + positional_parameter_names=_plot_args_replacer) def funcy(ax, *args, **kwargs): return "{args} | {kwargs}".format(args=args, kwargs=kwargs) data = {"x": "X", "y": "Y"} + assert_equal(funcy(None, "x", "y", data=data), + "('X', 'Y') | {}") + assert_equal(funcy(None, "x", "y", "c", data=data), + "('X', 'Y', 'c') | {}") assert_equal(funcy(None, "x", "y", "c", "x", "y", "x", "y", data=data), "('X', 'Y', 'c', 'X', 'Y', 'X', 'Y') | {}") + + # the color spec should not be in data... data = {"x": "X", "y": "Y", "c": "!!"} with assert_produces_warning(RuntimeWarning): assert_equal(funcy(None, "x", "y", "c", "x", "y", "x", "y", data=data), "('X', 'Y', '!!', 'X', 'Y', 'X', 'y') | {}") + + # And this is the case which we screw up, as we can't distinguish + # between a color spec and data... + # -> This test should actually produce a warning, if someone finds a way + # to distinguish between data and color spec... + with assert_produces_warning(False): + assert_equal(funcy(None, "x", "y", "c", "x", "y", "c", data=data), + "('X', 'Y', '!!', 'X', 'Y', '!!') | {}") From 77be3763190ec495e110a43fb459051bfadbfd5f Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Wed, 26 Aug 2015 13:59:41 +0200 Subject: [PATCH 34/60] FIX: add proper replace_names for some plotting methods --- lib/matplotlib/axes/_axes.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 626baf7adf36..11cf0f79c053 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -1098,7 +1098,10 @@ def vlines(self, x, ymin, ymax, colors='k', linestyles='solid', return coll - @unpack_labeled_data(replace_all_args=False, label_namer=None) + @unpack_labeled_data(replace_names=["positions", "lineoffsets", + "linelengths", "linewidths", + "colors", "linestyles"], + label_namer=None) @docstring.dedent_interpd def eventplot(self, positions, orientation='horizontal', lineoffsets=1, linelengths=1, linewidths=None, colors=None, @@ -2433,7 +2436,7 @@ def stem(self, *args, **kwargs): return stem_container - @unpack_labeled_data(replace_names=['x', 'labels', 'colors'], + @unpack_labeled_data(replace_names=['x', 'explode', 'labels', 'colors'], label_namer=None) def pie(self, x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, From 7a39ef6de4d564bf32cc7efda707e833f7051d4a Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Wed, 26 Aug 2015 14:00:22 +0200 Subject: [PATCH 35/60] TST: add testcases for some decorated plot methods These tests need to be either included in the original test if they use the same baseline image or use a differetn baseline image. Using two different test functions and the same baseline image results in races which kill ghostscript and others when they happen to access the same image. --- lib/matplotlib/tests/test_axes.py | 167 ++++++++++++++++++++++++++---- 1 file changed, 149 insertions(+), 18 deletions(-) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index e5c222183915..d03d91020b23 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -22,6 +22,10 @@ import warnings from matplotlib.cbook import IgnoredKeywordWarning +# Note: Some test cases are run twice: once normally and once with labeled data +# These two must be defined in the same test function or need to have +# different baseline images to prevent race conditions when nose runs +# the tests with multiple threads. @image_comparison(baseline_images=['formatter_ticker_001', 'formatter_ticker_002', @@ -274,7 +278,7 @@ def test_fill_units(): fig.autofmt_xdate() -@image_comparison(baseline_images=['single_point']) +@image_comparison(baseline_images=['single_point', 'single_point']) def test_single_point(): # Issue #1796: don't let lines.marker affect the grid matplotlib.rcParams['lines.marker'] = 'o' @@ -287,6 +291,16 @@ def test_single_point(): plt.subplot(212) plt.plot([1], [1], 'o') + # Reuse testcase from above for a labeled data test + data = {'a':[0], 'b':[1]} + + fig = plt.figure() + plt.subplot(211) + plt.plot('a', 'a', 'o', data=data) + + plt.subplot(212) + plt.plot('b','b', 'o', data=data) + @image_comparison(baseline_images=['single_date']) def test_single_date(): @@ -482,7 +496,7 @@ def test_axhspan_epoch(): ax.set_ylim(t0 - 5.0*dt, tf + 5.0*dt) -@image_comparison(baseline_images=['hexbin_extent'], +@image_comparison(baseline_images=['hexbin_extent', 'hexbin_extent'], remove_text=True, extensions=['png']) def test_hexbin_extent(): # this test exposes sf bug 2856228 @@ -495,6 +509,14 @@ def test_hexbin_extent(): ax.hexbin(x, y, extent=[.1, .3, .6, .7]) + # Reuse testcase from above for a labeled data test + data = {"x": x, "y": y} + + fig = plt.figure() + ax = fig.add_subplot(111) + ax.hexbin("x", "y", extent=[.1, .3, .6, .7], data=data) + + @image_comparison(baseline_images=['hexbin_empty'], remove_text=True, extensions=['png']) def test_hexbin_empty(): @@ -575,7 +597,7 @@ def test_nonfinite_limits(): ax.plot(x, y) -@image_comparison(baseline_images=['imshow'], +@image_comparison(baseline_images=['imshow', 'imshow'], remove_text=True) def test_imshow(): # Create a NxN image @@ -591,6 +613,12 @@ def test_imshow(): ax.imshow(r) + # Reuse testcase from above for a labeled data test + data={"r": r} + fig = plt.figure() + ax = fig.add_subplot(111) + ax.imshow("r", data=data) + @image_comparison(baseline_images=['imshow_clip']) def test_imshow_clip(): @@ -1011,13 +1039,21 @@ def test_marker_edges(): ax.plot(x+0.2, np.sin(x), 'y.', ms=30.0, mew=2, mec='b') -@image_comparison(baseline_images=['bar_tick_label_single'], +@image_comparison(baseline_images=['bar_tick_label_single', + 'bar_tick_label_single'], extensions=['png']) def test_bar_tick_label_single(): # From 2516: plot bar with array of string labels for x axis ax = plt.gca() ax.bar(0, 1 , tick_label='a') + # Reuse testcase from above for a labeled data test + data = {"a": 0, "b": 1} + fig = plt.figure() + ax = fig.add_subplot(111) + ax = plt.gca() + ax.bar("a", "b" , tick_label='a', data=data) + @image_comparison(baseline_images=['bar_tick_label_multiple'], extensions=['png']) @@ -1147,7 +1183,7 @@ def test_contour_colorbar(): cbar.add_lines(cs2, erase=False) -@image_comparison(baseline_images=['hist2d']) +@image_comparison(baseline_images=['hist2d', 'hist2d']) def test_hist2d(): np.random.seed(0) # make it not symetric in case we switch x and y axis @@ -1157,6 +1193,12 @@ def test_hist2d(): ax = fig.add_subplot(111) ax.hist2d(x, y, bins=10) + # Reuse testcase from above for a labeled data test + data = {"x": x, "y": y} + fig = plt.figure() + ax = fig.add_subplot(111) + ax.hist2d("x", "y", bins=10, data=data) + @image_comparison(baseline_images=['hist2d_transpose']) def test_hist2d_transpose(): @@ -1170,11 +1212,17 @@ def test_hist2d_transpose(): ax.hist2d(x, y, bins=10) -@image_comparison(baseline_images=['scatter']) +@image_comparison(baseline_images=['scatter', 'scatter']) def test_scatter_plot(): ax = plt.axes() - ax.scatter([3, 4, 2, 6], [2, 5, 2, 3], - c=['r', 'y', 'b', 'lime'], s=[24, 15, 19, 29]) + data = {"x": [3, 4, 2, 6], "y": [2, 5, 2, 3], "c": ['r', 'y', 'b', 'lime'], + "s": [24, 15, 19, 29]} + + ax.scatter(data["x"], data["y"], c=data["c"], s=data["s"]) + + # Reuse testcase from above for a labeled data test + ax = plt.axes() + ax.scatter("x", "y", c="c", s="s", data=data) @image_comparison(baseline_images=['scatter_marker'], remove_text=True, @@ -1252,7 +1300,8 @@ def test_log_scales(): ax.set_xscale('log', basex=9.0) -@image_comparison(baseline_images=['stackplot_test_image']) +@image_comparison(baseline_images=['stackplot_test_image', + 'stackplot_test_image']) def test_stackplot(): fig = plt.figure() x = np.linspace(0, 10, 10) @@ -1264,6 +1313,14 @@ def test_stackplot(): ax.set_xlim((0, 10)) ax.set_ylim((0, 70)) + # Reuse testcase from above for a labeled data test + data={"x": x, "y1": y1, "y2": y2, "y3": y3} + fig = plt.figure() + ax = fig.add_subplot(1, 1, 1) + ax.stackplot("x", "y1", "y2", "y3", data=data) + ax.set_xlim((0, 10)) + ax.set_ylim((0, 70)) + @image_comparison(baseline_images=['stackplot_test_baseline'], remove_text=True) @@ -1646,7 +1703,7 @@ def test_bxp_bad_positions(): assert_raises(ValueError, ax.bxp, logstats, positions=[2, 3]) -@image_comparison(baseline_images=['boxplot']) +@image_comparison(baseline_images=['boxplot', 'boxplot']) def test_boxplot(): x = np.linspace(-7, 7, 140) x = np.hstack([-25, x, 25]) @@ -1655,6 +1712,12 @@ def test_boxplot(): ax.boxplot([x, x], bootstrap=10000, notch=1) ax.set_ylim((-30, 30)) + # Reuse testcase from above for a labeled data test + data={"x": [x, x]} + fig, ax = plt.subplots() + ax.boxplot("x", bootstrap=10000, notch=1, data=data) + ax.set_ylim((-30, 30)) + @image_comparison(baseline_images=['boxplot_sym2'], remove_text=True, extensions=['png']) @@ -1834,7 +1897,8 @@ def test_boxplot_mod_artist_after_plotting(): obj.set_color('green') -@image_comparison(baseline_images=['violinplot_vert_baseline'], +@image_comparison(baseline_images=['violinplot_vert_baseline', + 'violinplot_vert_baseline'], extensions=['png']) def test_vert_violinplot_baseline(): # First 9 digits of frac(sqrt(2)) @@ -1844,6 +1908,13 @@ def test_vert_violinplot_baseline(): ax.violinplot(data, positions=range(4), showmeans=0, showextrema=0, showmedians=0) + # Reuse testcase from above for a labeled data test + data = {"d": data} + fig, ax = plt.subplots() + ax = plt.axes() + ax.violinplot("d", positions=range(4), showmeans=0, showextrema=0, + showmedians=0, data=data) + @image_comparison(baseline_images=['violinplot_vert_showmeans'], extensions=['png']) @@ -2021,7 +2092,8 @@ def test_manage_xticks(): assert_array_equal(old_xlim, new_xlim) -@image_comparison(baseline_images=['errorbar_basic', 'errorbar_mixed']) +@image_comparison(baseline_images=['errorbar_basic', 'errorbar_mixed', + 'errorbar_basic']) def test_errorbar(): x = np.arange(0.1, 4, 0.5) y = np.exp(-x) @@ -2065,6 +2137,15 @@ def test_errorbar(): fig.suptitle('Variable errorbars') + + # Reuse te first testcase from above for a labeled data test + data = {"x": x, "y": y} + fig = plt.figure() + ax = fig.gca() + ax.errorbar("x", "y", xerr=0.2, yerr=0.4, data=data) + ax.set_title("Simplest errorbars, 0.2 in x, 0.4 in y") + + @cleanup def test_errorbar_shape(): fig = plt.figure() @@ -2129,7 +2210,8 @@ def test_errorbar_limits(): ax.set_title('Errorbar upper and lower limits') -@image_comparison(baseline_images=['hist_stacked_stepfilled']) +@image_comparison(baseline_images=['hist_stacked_stepfilled', + 'hist_stacked_stepfilled']) def test_hist_stacked_stepfilled(): # make some data d1 = np.linspace(1, 3, 20) @@ -2138,6 +2220,12 @@ def test_hist_stacked_stepfilled(): ax = fig.add_subplot(111) ax.hist((d1, d2), histtype="stepfilled", stacked=True) + # Reuse testcase from above for a labeled data test + data = {"x": (d1, d2)} + fig = plt.figure() + ax = fig.add_subplot(111) + ax.hist("x", histtype="stepfilled", stacked=True, data=data) + @image_comparison(baseline_images=['hist_offset']) def test_hist_offset(): @@ -2386,7 +2474,7 @@ def test_alpha(): markersize=20, lw=10) -@image_comparison(baseline_images=['eventplot'], remove_text=True) +@image_comparison(baseline_images=['eventplot', 'eventplot'], remove_text=True) def test_eventplot(): ''' test that eventplot produces the correct output @@ -2423,6 +2511,15 @@ def test_eventplot(): num_collections = len(colls) np.testing.assert_equal(num_collections, num_datasets) + # Reuse testcase from above for a labeled data test + data = {"pos": data, "c": colors, "lo": lineoffsets, "ll": linelengths} + fig = plt.figure() + axobj = fig.add_subplot(111) + colls = axobj.eventplot("pos", colors="c", lineoffsets="lo", + linelengths="ll", data=data) + num_collections = len(colls) + np.testing.assert_equal(num_collections, num_datasets) + @image_comparison(baseline_images=['test_eventplot_defaults'], extensions=['png'], remove_text=True) def test_eventplot_defaults(): @@ -2543,7 +2640,8 @@ def test_eb_line_zorder(): ax.set_title("errorbar zorder test") -@image_comparison(baseline_images=['step_linestyle'], remove_text=True) +@image_comparison(baseline_images=['step_linestyle', 'step_linestyle'], + remove_text=True) def test_step_linestyle(): x = y = np.arange(10) @@ -2560,6 +2658,18 @@ def test_step_linestyle(): ax.set_xlim([-1, 5]) ax.set_ylim([-1, 7]) + # Reuse testcase from above for a labeled data test + data = {"x": x, "y": y, "y1": y+1, "y2": y+2} + fig, ax_lst = plt.subplots(2, 2) + ax_lst = ax_lst.flatten() + ln_styles = ['-', '--', '-.', ':'] + for ax, ls in zip(ax_lst, ln_styles): + ax.step("x", "y", lw=5, linestyle=ls, where='pre', data=data) + ax.step("x", "y1", lw=5, linestyle=ls, where='mid', data=data) + ax.step("x", "y2", lw=5, linestyle=ls, where='post', data=data) + ax.set_xlim([-1, 5]) + ax.set_ylim([-1, 7]) + @image_comparison(baseline_images=['mixed_collection'], remove_text=True) def test_mixed_collection(): @@ -3501,8 +3611,8 @@ def make_patch_spines_invisible(ax): host.tick_params(axis='x', **tkw) -@image_comparison(baseline_images=['twin_spines_on_top'], extensions=['png'], - remove_text=True) +@image_comparison(baseline_images=['twin_spines_on_top', 'twin_spines_on_top'], + extensions=['png'], remove_text=True) def test_twin_spines_on_top(): matplotlib.rcParams['axes.linewidth'] = 48.0 matplotlib.rcParams['lines.linewidth'] = 48.0 @@ -3521,6 +3631,16 @@ def test_twin_spines_on_top(): ax2.plot(data[0], data[1]/1E3, color='#7FC97F') ax2.fill_between(data[0], data[1]/1E3, color='#7FC97F', alpha=.5) + # Reuse testcase from above for a labeled data test + data = {"x": data[0], "y": data[1]/1E3} + fig = plt.figure() + ax1 = fig.add_subplot(1, 1, 1) + ax2 = ax1.twinx() + ax1.plot("x", "y", color='#BEAED4', data=data) + ax1.fill_between("x", "y", color='#BEAED4', alpha=.8, data=data) + ax2.plot("x", "y", color='#7FC97F', data=data) + ax2.fill_between("x", "y", color='#7FC97F', alpha=.5, data=data) + @cleanup def test_rcparam_grid_minor(): @@ -3606,7 +3726,8 @@ def test_text_labelsize(): ax.tick_params(direction='out') -@image_comparison(baseline_images=['pie_linewidth_0'], extensions=['png']) +@image_comparison(baseline_images=['pie_linewidth_0', 'pie_linewidth_0'], + extensions=['png']) def test_pie_linewidth_0(): # The slices will be ordered and plotted counter-clockwise. labels = 'Frogs', 'Hogs', 'Dogs', 'Logs' @@ -3620,6 +3741,16 @@ def test_pie_linewidth_0(): # Set aspect ratio to be equal so that pie is drawn as a circle. plt.axis('equal') + # Reuse testcase from above for a labeled data test + data = {"l": labels, "s": sizes, "c": colors, "ex": explode} + # FIXME: Workaround until the pyplot is regenerated + fig = plt.figure() + ax = fig.gca() + ax.pie("s", explode="ex", labels="l", colors="c", + autopct='%1.1f%%', shadow=True, startangle=90, + wedgeprops={'linewidth': 0}, data=data) + ax.axis('equal') + @image_comparison(baseline_images=['pie_center_radius'], extensions=['png']) def test_pie_center_radius(): From a0c738e852cca03f1569fa878c7d51842f9ac3ed Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Wed, 26 Aug 2015 21:51:36 +0200 Subject: [PATCH 36/60] MNT: regenerate pyplot to get the data kwarg --- lib/matplotlib/pyplot.py | 295 ++++++++++++--------------------------- 1 file changed, 91 insertions(+), 204 deletions(-) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 39d4c3858c61..12c429641349 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -2508,15 +2508,15 @@ def spy(Z, precision=0, marker=None, markersize=None, aspect='equal', hold=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.acorr) -def acorr(x, hold=None, **kwargs): +def acorr(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.acorr(x, **kwargs) + ret = ax.acorr(*args, **kwargs) finally: ax.hold(washold) @@ -2525,17 +2525,15 @@ def acorr(x, hold=None, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.angle_spectrum) -def angle_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, - hold=None, **kwargs): +def angle_spectrum(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.angle_spectrum(x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, - sides=sides, **kwargs) + ret = ax.angle_spectrum(*args, **kwargs) finally: ax.hold(washold) @@ -2629,15 +2627,15 @@ def axvspan(xmin, xmax, ymin=0, ymax=1, hold=None, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.bar) -def bar(left, height, width=0.8, bottom=None, hold=None, **kwargs): +def bar(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.bar(left, height, width=width, bottom=bottom, **kwargs) + ret = ax.bar(*args, **kwargs) finally: ax.hold(washold) @@ -2663,15 +2661,15 @@ def barh(bottom, width, height=0.8, left=None, hold=None, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.broken_barh) -def broken_barh(xranges, yrange, hold=None, **kwargs): +def broken_barh(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.broken_barh(xranges, yrange, **kwargs) + ret = ax.broken_barh(*args, **kwargs) finally: ax.hold(washold) @@ -2680,30 +2678,15 @@ def broken_barh(xranges, yrange, hold=None, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.boxplot) -def boxplot(x, notch=None, sym=None, vert=None, whis=None, positions=None, - widths=None, patch_artist=None, bootstrap=None, usermedians=None, - conf_intervals=None, meanline=None, showmeans=None, showcaps=None, - showbox=None, showfliers=None, boxprops=None, labels=None, - flierprops=None, medianprops=None, meanprops=None, capprops=None, - whiskerprops=None, manage_xticks=True, hold=None): +def boxplot(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.boxplot(x, notch=notch, sym=sym, vert=vert, whis=whis, - positions=positions, widths=widths, - patch_artist=patch_artist, bootstrap=bootstrap, - usermedians=usermedians, - conf_intervals=conf_intervals, meanline=meanline, - showmeans=showmeans, showcaps=showcaps, - showbox=showbox, showfliers=showfliers, - boxprops=boxprops, labels=labels, - flierprops=flierprops, medianprops=medianprops, - meanprops=meanprops, capprops=capprops, - whiskerprops=whiskerprops, manage_xticks=manage_xticks) + ret = ax.boxplot(*args, **kwargs) finally: ax.hold(washold) @@ -2712,19 +2695,15 @@ def boxplot(x, notch=None, sym=None, vert=None, whis=None, positions=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.cohere) -def cohere(x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, - window=mlab.window_hanning, noverlap=0, pad_to=None, sides='default', - scale_by_freq=None, hold=None, **kwargs): +def cohere(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.cohere(x, y, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, - window=window, noverlap=noverlap, pad_to=pad_to, - sides=sides, scale_by_freq=scale_by_freq, **kwargs) + ret = ax.cohere(*args, **kwargs) finally: ax.hold(washold) @@ -2784,20 +2763,15 @@ def contourf(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.csd) -def csd(x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, - noverlap=None, pad_to=None, sides=None, scale_by_freq=None, - return_line=None, hold=None, **kwargs): +def csd(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.csd(x, y, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, - window=window, noverlap=noverlap, pad_to=pad_to, - sides=sides, scale_by_freq=scale_by_freq, - return_line=return_line, **kwargs) + ret = ax.csd(*args, **kwargs) finally: ax.hold(washold) @@ -2806,22 +2780,15 @@ def csd(x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.errorbar) -def errorbar(x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, - capsize=None, barsabove=False, lolims=False, uplims=False, - xlolims=False, xuplims=False, errorevery=1, capthick=None, - hold=None, **kwargs): +def errorbar(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.errorbar(x, y, yerr=yerr, xerr=xerr, fmt=fmt, ecolor=ecolor, - elinewidth=elinewidth, capsize=capsize, - barsabove=barsabove, lolims=lolims, uplims=uplims, - xlolims=xlolims, xuplims=xuplims, - errorevery=errorevery, capthick=capthick, **kwargs) + ret = ax.errorbar(*args, **kwargs) finally: ax.hold(washold) @@ -2830,20 +2797,15 @@ def errorbar(x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.eventplot) -def eventplot(positions, orientation='horizontal', lineoffsets=1, linelengths=1, - linewidths=None, colors=None, linestyles='solid', hold=None, - **kwargs): +def eventplot(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.eventplot(positions, orientation=orientation, - lineoffsets=lineoffsets, linelengths=linelengths, - linewidths=linewidths, colors=colors, - linestyles=linestyles, **kwargs) + ret = ax.eventplot(*args, **kwargs) finally: ax.hold(washold) @@ -2869,16 +2831,15 @@ def fill(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.fill_between) -def fill_between(x, y1, y2=0, where=None, interpolate=False, hold=None, **kwargs): +def fill_between(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.fill_between(x, y1, y2=y2, where=where, - interpolate=interpolate, **kwargs) + ret = ax.fill_between(*args, **kwargs) finally: ax.hold(washold) @@ -2887,15 +2848,15 @@ def fill_between(x, y1, y2=0, where=None, interpolate=False, hold=None, **kwargs # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.fill_betweenx) -def fill_betweenx(y, x1, x2=0, where=None, hold=None, **kwargs): +def fill_betweenx(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.fill_betweenx(y, x1, x2=x2, where=where, **kwargs) + ret = ax.fill_betweenx(*args, **kwargs) finally: ax.hold(washold) @@ -2904,24 +2865,15 @@ def fill_betweenx(y, x1, x2=0, where=None, hold=None, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.hexbin) -def hexbin(x, y, C=None, gridsize=100, bins=None, xscale='linear', - yscale='linear', extent=None, cmap=None, norm=None, vmin=None, - vmax=None, alpha=None, linewidths=None, edgecolors='none', - reduce_C_function=np.mean, mincnt=None, marginals=False, hold=None, - **kwargs): +def hexbin(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.hexbin(x, y, C=C, gridsize=gridsize, bins=bins, xscale=xscale, - yscale=yscale, extent=extent, cmap=cmap, norm=norm, - vmin=vmin, vmax=vmax, alpha=alpha, - linewidths=linewidths, edgecolors=edgecolors, - reduce_C_function=reduce_C_function, mincnt=mincnt, - marginals=marginals, **kwargs) + ret = ax.hexbin(*args, **kwargs) finally: ax.hold(washold) sci(ret) @@ -2930,22 +2882,15 @@ def hexbin(x, y, C=None, gridsize=100, bins=None, xscale='linear', # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.hist) -def hist(x, bins=10, range=None, normed=False, weights=None, cumulative=False, - bottom=None, histtype='bar', align='mid', orientation='vertical', - rwidth=None, log=False, color=None, label=None, stacked=False, - hold=None, **kwargs): +def hist(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.hist(x, bins=bins, range=range, normed=normed, - weights=weights, cumulative=cumulative, bottom=bottom, - histtype=histtype, align=align, orientation=orientation, - rwidth=rwidth, log=log, color=color, label=label, - stacked=stacked, **kwargs) + ret = ax.hist(*args, **kwargs) finally: ax.hold(washold) @@ -2954,17 +2899,15 @@ def hist(x, bins=10, range=None, normed=False, weights=None, cumulative=False, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.hist2d) -def hist2d(x, y, bins=10, range=None, normed=False, weights=None, cmin=None, - cmax=None, hold=None, **kwargs): +def hist2d(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.hist2d(x, y, bins=bins, range=range, normed=normed, - weights=weights, cmin=cmin, cmax=cmax, **kwargs) + ret = ax.hist2d(*args, **kwargs) finally: ax.hold(washold) sci(ret[-1]) @@ -2973,17 +2916,15 @@ def hist2d(x, y, bins=10, range=None, normed=False, weights=None, cmin=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.hlines) -def hlines(y, xmin, xmax, colors='k', linestyles='solid', label='', hold=None, - **kwargs): +def hlines(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.hlines(y, xmin, xmax, colors=colors, linestyles=linestyles, - label=label, **kwargs) + ret = ax.hlines(*args, **kwargs) finally: ax.hold(washold) @@ -2992,22 +2933,15 @@ def hlines(y, xmin, xmax, colors='k', linestyles='solid', label='', hold=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.imshow) -def imshow(X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, - vmin=None, vmax=None, origin=None, extent=None, shape=None, - filternorm=1, filterrad=4.0, imlim=None, resample=None, url=None, - hold=None, **kwargs): +def imshow(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.imshow(X, cmap=cmap, norm=norm, aspect=aspect, - interpolation=interpolation, alpha=alpha, vmin=vmin, - vmax=vmax, origin=origin, extent=extent, shape=shape, - filternorm=filternorm, filterrad=filterrad, - imlim=imlim, resample=resample, url=url, **kwargs) + ret = ax.imshow(*args, **kwargs) finally: ax.hold(washold) sci(ret) @@ -3033,18 +2967,15 @@ def loglog(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.magnitude_spectrum) -def magnitude_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, - sides=None, scale=None, hold=None, **kwargs): +def magnitude_spectrum(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.magnitude_spectrum(x, Fs=Fs, Fc=Fc, window=window, - pad_to=pad_to, sides=sides, scale=scale, - **kwargs) + ret = ax.magnitude_spectrum(*args, **kwargs) finally: ax.hold(washold) @@ -3087,17 +3018,15 @@ def pcolormesh(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.phase_spectrum) -def phase_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, - hold=None, **kwargs): +def phase_spectrum(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.phase_spectrum(x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, - sides=sides, **kwargs) + ret = ax.phase_spectrum(*args, **kwargs) finally: ax.hold(washold) @@ -3106,23 +3035,15 @@ def phase_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.pie) -def pie(x, explode=None, labels=None, colors=None, autopct=None, - pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, - radius=None, counterclock=True, wedgeprops=None, textprops=None, - center=(0, 0), frame=False, hold=None): +def pie(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.pie(x, explode=explode, labels=labels, colors=colors, - autopct=autopct, pctdistance=pctdistance, shadow=shadow, - labeldistance=labeldistance, startangle=startangle, - radius=radius, counterclock=counterclock, - wedgeprops=wedgeprops, textprops=textprops, center=center, - frame=frame) + ret = ax.pie(*args, **kwargs) finally: ax.hold(washold) @@ -3148,17 +3069,15 @@ def plot(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.plot_date) -def plot_date(x, y, fmt='o', tz=None, xdate=True, ydate=False, hold=None, - **kwargs): +def plot_date(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.plot_date(x, y, fmt=fmt, tz=tz, xdate=xdate, ydate=ydate, - **kwargs) + ret = ax.plot_date(*args, **kwargs) finally: ax.hold(washold) @@ -3167,20 +3086,15 @@ def plot_date(x, y, fmt='o', tz=None, xdate=True, ydate=False, hold=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.psd) -def psd(x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, - noverlap=None, pad_to=None, sides=None, scale_by_freq=None, - return_line=None, hold=None, **kwargs): +def psd(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.psd(x, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, - window=window, noverlap=noverlap, pad_to=pad_to, - sides=sides, scale_by_freq=scale_by_freq, - return_line=return_line, **kwargs) + ret = ax.psd(*args, **kwargs) finally: ax.hold(washold) @@ -3189,15 +3103,15 @@ def psd(x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.quiver) -def quiver(*args, **kw): +def quiver(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kw.pop('hold', None) + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.quiver(*args, **kw) + ret = ax.quiver(*args, **kwargs) finally: ax.hold(washold) sci(ret) @@ -3223,20 +3137,15 @@ def quiverkey(*args, **kw): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.scatter) -def scatter(x, y, s=20, c=None, marker='o', cmap=None, norm=None, vmin=None, - vmax=None, alpha=None, linewidths=None, verts=None, edgecolors=None, - hold=None, **kwargs): +def scatter(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.scatter(x, y, s=s, c=c, marker=marker, cmap=cmap, norm=norm, - vmin=vmin, vmax=vmax, alpha=alpha, - linewidths=linewidths, verts=verts, - edgecolors=edgecolors, **kwargs) + ret = ax.scatter(*args, **kwargs) finally: ax.hold(washold) sci(ret) @@ -3279,22 +3188,15 @@ def semilogy(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.specgram) -def specgram(x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, - noverlap=None, cmap=None, xextent=None, pad_to=None, sides=None, - scale_by_freq=None, mode=None, scale=None, vmin=None, vmax=None, - hold=None, **kwargs): +def specgram(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.specgram(x, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, - window=window, noverlap=noverlap, cmap=cmap, - xextent=xextent, pad_to=pad_to, sides=sides, - scale_by_freq=scale_by_freq, mode=mode, scale=scale, - vmin=vmin, vmax=vmax, **kwargs) + ret = ax.specgram(*args, **kwargs) finally: ax.hold(washold) sci(ret[-1]) @@ -3303,7 +3205,7 @@ def specgram(x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.stackplot) -def stackplot(x, *args, **kwargs): +def stackplot(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3311,7 +3213,7 @@ def stackplot(x, *args, **kwargs): if hold is not None: ax.hold(hold) try: - ret = ax.stackplot(x, *args, **kwargs) + ret = ax.stackplot(*args, **kwargs) finally: ax.hold(washold) @@ -3337,7 +3239,7 @@ def stem(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.step) -def step(x, y, *args, **kwargs): +def step(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3345,7 +3247,7 @@ def step(x, y, *args, **kwargs): if hold is not None: ax.hold(hold) try: - ret = ax.step(x, y, *args, **kwargs) + ret = ax.step(*args, **kwargs) finally: ax.hold(washold) @@ -3354,21 +3256,15 @@ def step(x, y, *args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.streamplot) -def streamplot(x, y, u, v, density=1, linewidth=None, color=None, cmap=None, - norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, - transform=None, zorder=1, start_points=None, hold=None): +def streamplot(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.streamplot(x, y, u, v, density=density, linewidth=linewidth, - color=color, cmap=cmap, norm=norm, - arrowsize=arrowsize, arrowstyle=arrowstyle, - minlength=minlength, transform=transform, - zorder=zorder, start_points=start_points) + ret = ax.streamplot(*args, **kwargs) finally: ax.hold(washold) sci(ret.lines) @@ -3445,20 +3341,15 @@ def triplot(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.violinplot) -def violinplot(dataset, positions=None, vert=True, widths=0.5, showmeans=False, - showextrema=True, showmedians=False, points=100, bw_method=None, - hold=None): +def violinplot(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.violinplot(dataset, positions=positions, vert=vert, - widths=widths, showmeans=showmeans, - showextrema=showextrema, showmedians=showmedians, - points=points, bw_method=bw_method) + ret = ax.violinplot(*args, **kwargs) finally: ax.hold(washold) @@ -3467,17 +3358,15 @@ def violinplot(dataset, positions=None, vert=True, widths=0.5, showmeans=False, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.vlines) -def vlines(x, ymin, ymax, colors='k', linestyles='solid', label='', hold=None, - **kwargs): +def vlines(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.vlines(x, ymin, ymax, colors=colors, linestyles=linestyles, - label=label, **kwargs) + ret = ax.vlines(*args, **kwargs) finally: ax.hold(washold) @@ -3486,17 +3375,15 @@ def vlines(x, ymin, ymax, colors='k', linestyles='solid', label='', hold=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.xcorr) -def xcorr(x, y, normed=True, detrend=mlab.detrend_none, usevlines=True, - maxlags=10, hold=None, **kwargs): +def xcorr(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.xcorr(x, y, normed=normed, detrend=detrend, - usevlines=usevlines, maxlags=maxlags, **kwargs) + ret = ax.xcorr(*args, **kwargs) finally: ax.hold(washold) @@ -3505,15 +3392,15 @@ def xcorr(x, y, normed=True, detrend=mlab.detrend_none, usevlines=True, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.barbs) -def barbs(*args, **kw): +def barbs(*args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kw.pop('hold', None) + hold = kwargs.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.barbs(*args, **kw) + ret = ax.barbs(*args, **kwargs) finally: ax.hold(washold) From 7a6d44d35cb663da0b7a08c5692bf0599b9e3443 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 26 Aug 2015 20:40:09 -0400 Subject: [PATCH 37/60] MNT: make boilerplate.py a bit smarter Check if there is a `__wrapped__` attribute and if there is use that for the to extract the signature from. --- boilerplate.py | 7 +- lib/matplotlib/pyplot.py | 296 +++++++++++++++++++++++++++------------ 2 files changed, 211 insertions(+), 92 deletions(-) diff --git a/boilerplate.py b/boilerplate.py index b0fce8ac0113..be2cd52d87e1 100644 --- a/boilerplate.py +++ b/boilerplate.py @@ -209,7 +209,12 @@ def format_value(value): mappable = '' # Get argspec of wrapped function - args, varargs, varkw, defaults = inspect.getargspec(getattr(Axes, func)) + work_func = getattr(Axes, func) + if hasattr(work_func, '__wrapped__'): + work_func = work_func.__wrapped__ + + args, varargs, varkw, defaults = inspect.getargspec(work_func) + args.pop(0) # remove 'self' argument if defaults is None: defaults = () diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 12c429641349..af52b1f8817e 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -2508,15 +2508,15 @@ def spy(Z, precision=0, marker=None, markersize=None, aspect='equal', hold=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.acorr) -def acorr(*args, **kwargs): +def acorr(x, hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.acorr(*args, **kwargs) + ret = ax.acorr(x, **kwargs) finally: ax.hold(washold) @@ -2525,15 +2525,17 @@ def acorr(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.angle_spectrum) -def angle_spectrum(*args, **kwargs): +def angle_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, + hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.angle_spectrum(*args, **kwargs) + ret = ax.angle_spectrum(x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, + sides=sides, **kwargs) finally: ax.hold(washold) @@ -2627,15 +2629,15 @@ def axvspan(xmin, xmax, ymin=0, ymax=1, hold=None, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.bar) -def bar(*args, **kwargs): +def bar(left, height, width=0.8, bottom=None, hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.bar(*args, **kwargs) + ret = ax.bar(left, height, width=width, bottom=bottom, **kwargs) finally: ax.hold(washold) @@ -2661,15 +2663,15 @@ def barh(bottom, width, height=0.8, left=None, hold=None, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.broken_barh) -def broken_barh(*args, **kwargs): +def broken_barh(xranges, yrange, hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.broken_barh(*args, **kwargs) + ret = ax.broken_barh(xranges, yrange, **kwargs) finally: ax.hold(washold) @@ -2678,15 +2680,30 @@ def broken_barh(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.boxplot) -def boxplot(*args, **kwargs): +def boxplot(x, notch=None, sym=None, vert=None, whis=None, positions=None, + widths=None, patch_artist=None, bootstrap=None, usermedians=None, + conf_intervals=None, meanline=None, showmeans=None, showcaps=None, + showbox=None, showfliers=None, boxprops=None, labels=None, + flierprops=None, medianprops=None, meanprops=None, capprops=None, + whiskerprops=None, manage_xticks=True, hold=None): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.boxplot(*args, **kwargs) + ret = ax.boxplot(x, notch=notch, sym=sym, vert=vert, whis=whis, + positions=positions, widths=widths, + patch_artist=patch_artist, bootstrap=bootstrap, + usermedians=usermedians, + conf_intervals=conf_intervals, meanline=meanline, + showmeans=showmeans, showcaps=showcaps, + showbox=showbox, showfliers=showfliers, + boxprops=boxprops, labels=labels, + flierprops=flierprops, medianprops=medianprops, + meanprops=meanprops, capprops=capprops, + whiskerprops=whiskerprops, manage_xticks=manage_xticks) finally: ax.hold(washold) @@ -2695,15 +2712,19 @@ def boxplot(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.cohere) -def cohere(*args, **kwargs): +def cohere(x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, + window=mlab.window_hanning, noverlap=0, pad_to=None, sides='default', + scale_by_freq=None, hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.cohere(*args, **kwargs) + ret = ax.cohere(x, y, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, + window=window, noverlap=noverlap, pad_to=pad_to, + sides=sides, scale_by_freq=scale_by_freq, **kwargs) finally: ax.hold(washold) @@ -2763,15 +2784,20 @@ def contourf(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.csd) -def csd(*args, **kwargs): +def csd(x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, + noverlap=None, pad_to=None, sides=None, scale_by_freq=None, + return_line=None, hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.csd(*args, **kwargs) + ret = ax.csd(x, y, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, + window=window, noverlap=noverlap, pad_to=pad_to, + sides=sides, scale_by_freq=scale_by_freq, + return_line=return_line, **kwargs) finally: ax.hold(washold) @@ -2780,15 +2806,22 @@ def csd(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.errorbar) -def errorbar(*args, **kwargs): +def errorbar(x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, + capsize=None, barsabove=False, lolims=False, uplims=False, + xlolims=False, xuplims=False, errorevery=1, capthick=None, + hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.errorbar(*args, **kwargs) + ret = ax.errorbar(x, y, yerr=yerr, xerr=xerr, fmt=fmt, ecolor=ecolor, + elinewidth=elinewidth, capsize=capsize, + barsabove=barsabove, lolims=lolims, uplims=uplims, + xlolims=xlolims, xuplims=xuplims, + errorevery=errorevery, capthick=capthick, **kwargs) finally: ax.hold(washold) @@ -2797,15 +2830,20 @@ def errorbar(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.eventplot) -def eventplot(*args, **kwargs): +def eventplot(positions, orientation='horizontal', lineoffsets=1, linelengths=1, + linewidths=None, colors=None, linestyles='solid', hold=None, + **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.eventplot(*args, **kwargs) + ret = ax.eventplot(positions, orientation=orientation, + lineoffsets=lineoffsets, linelengths=linelengths, + linewidths=linewidths, colors=colors, + linestyles=linestyles, **kwargs) finally: ax.hold(washold) @@ -2831,15 +2869,17 @@ def fill(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.fill_between) -def fill_between(*args, **kwargs): +def fill_between(x, y1, y2=0, where=None, interpolate=False, step=None, + hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.fill_between(*args, **kwargs) + ret = ax.fill_between(x, y1, y2=y2, where=where, + interpolate=interpolate, step=step, **kwargs) finally: ax.hold(washold) @@ -2848,15 +2888,15 @@ def fill_between(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.fill_betweenx) -def fill_betweenx(*args, **kwargs): +def fill_betweenx(y, x1, x2=0, where=None, step=None, hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.fill_betweenx(*args, **kwargs) + ret = ax.fill_betweenx(y, x1, x2=x2, where=where, step=step, **kwargs) finally: ax.hold(washold) @@ -2865,15 +2905,24 @@ def fill_betweenx(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.hexbin) -def hexbin(*args, **kwargs): +def hexbin(x, y, C=None, gridsize=100, bins=None, xscale='linear', + yscale='linear', extent=None, cmap=None, norm=None, vmin=None, + vmax=None, alpha=None, linewidths=None, edgecolors='none', + reduce_C_function=np.mean, mincnt=None, marginals=False, hold=None, + **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.hexbin(*args, **kwargs) + ret = ax.hexbin(x, y, C=C, gridsize=gridsize, bins=bins, xscale=xscale, + yscale=yscale, extent=extent, cmap=cmap, norm=norm, + vmin=vmin, vmax=vmax, alpha=alpha, + linewidths=linewidths, edgecolors=edgecolors, + reduce_C_function=reduce_C_function, mincnt=mincnt, + marginals=marginals, **kwargs) finally: ax.hold(washold) sci(ret) @@ -2882,15 +2931,22 @@ def hexbin(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.hist) -def hist(*args, **kwargs): +def hist(x, bins=10, range=None, normed=False, weights=None, cumulative=False, + bottom=None, histtype='bar', align='mid', orientation='vertical', + rwidth=None, log=False, color=None, label=None, stacked=False, + hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.hist(*args, **kwargs) + ret = ax.hist(x, bins=bins, range=range, normed=normed, + weights=weights, cumulative=cumulative, bottom=bottom, + histtype=histtype, align=align, orientation=orientation, + rwidth=rwidth, log=log, color=color, label=label, + stacked=stacked, **kwargs) finally: ax.hold(washold) @@ -2899,15 +2955,17 @@ def hist(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.hist2d) -def hist2d(*args, **kwargs): +def hist2d(x, y, bins=10, range=None, normed=False, weights=None, cmin=None, + cmax=None, hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.hist2d(*args, **kwargs) + ret = ax.hist2d(x, y, bins=bins, range=range, normed=normed, + weights=weights, cmin=cmin, cmax=cmax, **kwargs) finally: ax.hold(washold) sci(ret[-1]) @@ -2916,15 +2974,17 @@ def hist2d(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.hlines) -def hlines(*args, **kwargs): +def hlines(y, xmin, xmax, colors='k', linestyles='solid', label='', hold=None, + **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.hlines(*args, **kwargs) + ret = ax.hlines(y, xmin, xmax, colors=colors, linestyles=linestyles, + label=label, **kwargs) finally: ax.hold(washold) @@ -2933,15 +2993,22 @@ def hlines(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.imshow) -def imshow(*args, **kwargs): +def imshow(X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, + vmin=None, vmax=None, origin=None, extent=None, shape=None, + filternorm=1, filterrad=4.0, imlim=None, resample=None, url=None, + hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.imshow(*args, **kwargs) + ret = ax.imshow(X, cmap=cmap, norm=norm, aspect=aspect, + interpolation=interpolation, alpha=alpha, vmin=vmin, + vmax=vmax, origin=origin, extent=extent, shape=shape, + filternorm=filternorm, filterrad=filterrad, + imlim=imlim, resample=resample, url=url, **kwargs) finally: ax.hold(washold) sci(ret) @@ -2967,15 +3034,18 @@ def loglog(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.magnitude_spectrum) -def magnitude_spectrum(*args, **kwargs): +def magnitude_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, + sides=None, scale=None, hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.magnitude_spectrum(*args, **kwargs) + ret = ax.magnitude_spectrum(x, Fs=Fs, Fc=Fc, window=window, + pad_to=pad_to, sides=sides, scale=scale, + **kwargs) finally: ax.hold(washold) @@ -3018,15 +3088,17 @@ def pcolormesh(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.phase_spectrum) -def phase_spectrum(*args, **kwargs): +def phase_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, + hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.phase_spectrum(*args, **kwargs) + ret = ax.phase_spectrum(x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, + sides=sides, **kwargs) finally: ax.hold(washold) @@ -3035,15 +3107,23 @@ def phase_spectrum(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.pie) -def pie(*args, **kwargs): +def pie(x, explode=None, labels=None, colors=None, autopct=None, + pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, + radius=None, counterclock=True, wedgeprops=None, textprops=None, + center=(0, 0), frame=False, hold=None): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.pie(*args, **kwargs) + ret = ax.pie(x, explode=explode, labels=labels, colors=colors, + autopct=autopct, pctdistance=pctdistance, shadow=shadow, + labeldistance=labeldistance, startangle=startangle, + radius=radius, counterclock=counterclock, + wedgeprops=wedgeprops, textprops=textprops, center=center, + frame=frame) finally: ax.hold(washold) @@ -3069,15 +3149,17 @@ def plot(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.plot_date) -def plot_date(*args, **kwargs): +def plot_date(x, y, fmt='o', tz=None, xdate=True, ydate=False, hold=None, + **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.plot_date(*args, **kwargs) + ret = ax.plot_date(x, y, fmt=fmt, tz=tz, xdate=xdate, ydate=ydate, + **kwargs) finally: ax.hold(washold) @@ -3086,15 +3168,20 @@ def plot_date(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.psd) -def psd(*args, **kwargs): +def psd(x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, + noverlap=None, pad_to=None, sides=None, scale_by_freq=None, + return_line=None, hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.psd(*args, **kwargs) + ret = ax.psd(x, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, + window=window, noverlap=noverlap, pad_to=pad_to, + sides=sides, scale_by_freq=scale_by_freq, + return_line=return_line, **kwargs) finally: ax.hold(washold) @@ -3103,15 +3190,15 @@ def psd(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.quiver) -def quiver(*args, **kwargs): +def quiver(*args, **kw): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + hold = kw.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.quiver(*args, **kwargs) + ret = ax.quiver(*args, **kw) finally: ax.hold(washold) sci(ret) @@ -3137,15 +3224,20 @@ def quiverkey(*args, **kw): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.scatter) -def scatter(*args, **kwargs): +def scatter(x, y, s=20, c=None, marker='o', cmap=None, norm=None, vmin=None, + vmax=None, alpha=None, linewidths=None, verts=None, edgecolors=None, + hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.scatter(*args, **kwargs) + ret = ax.scatter(x, y, s=s, c=c, marker=marker, cmap=cmap, norm=norm, + vmin=vmin, vmax=vmax, alpha=alpha, + linewidths=linewidths, verts=verts, + edgecolors=edgecolors, **kwargs) finally: ax.hold(washold) sci(ret) @@ -3188,15 +3280,22 @@ def semilogy(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.specgram) -def specgram(*args, **kwargs): +def specgram(x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, + noverlap=None, cmap=None, xextent=None, pad_to=None, sides=None, + scale_by_freq=None, mode=None, scale=None, vmin=None, vmax=None, + hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.specgram(*args, **kwargs) + ret = ax.specgram(x, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, + window=window, noverlap=noverlap, cmap=cmap, + xextent=xextent, pad_to=pad_to, sides=sides, + scale_by_freq=scale_by_freq, mode=mode, scale=scale, + vmin=vmin, vmax=vmax, **kwargs) finally: ax.hold(washold) sci(ret[-1]) @@ -3205,7 +3304,7 @@ def specgram(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.stackplot) -def stackplot(*args, **kwargs): +def stackplot(x, *args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3213,7 +3312,7 @@ def stackplot(*args, **kwargs): if hold is not None: ax.hold(hold) try: - ret = ax.stackplot(*args, **kwargs) + ret = ax.stackplot(x, *args, **kwargs) finally: ax.hold(washold) @@ -3239,7 +3338,7 @@ def stem(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.step) -def step(*args, **kwargs): +def step(x, y, *args, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3247,7 +3346,7 @@ def step(*args, **kwargs): if hold is not None: ax.hold(hold) try: - ret = ax.step(*args, **kwargs) + ret = ax.step(x, y, *args, **kwargs) finally: ax.hold(washold) @@ -3256,15 +3355,21 @@ def step(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.streamplot) -def streamplot(*args, **kwargs): +def streamplot(x, y, u, v, density=1, linewidth=None, color=None, cmap=None, + norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, + transform=None, zorder=1, start_points=None, hold=None): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.streamplot(*args, **kwargs) + ret = ax.streamplot(x, y, u, v, density=density, linewidth=linewidth, + color=color, cmap=cmap, norm=norm, + arrowsize=arrowsize, arrowstyle=arrowstyle, + minlength=minlength, transform=transform, + zorder=zorder, start_points=start_points) finally: ax.hold(washold) sci(ret.lines) @@ -3341,15 +3446,20 @@ def triplot(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.violinplot) -def violinplot(*args, **kwargs): +def violinplot(dataset, positions=None, vert=True, widths=0.5, showmeans=False, + showextrema=True, showmedians=False, points=100, bw_method=None, + hold=None): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.violinplot(*args, **kwargs) + ret = ax.violinplot(dataset, positions=positions, vert=vert, + widths=widths, showmeans=showmeans, + showextrema=showextrema, showmedians=showmedians, + points=points, bw_method=bw_method) finally: ax.hold(washold) @@ -3358,15 +3468,17 @@ def violinplot(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.vlines) -def vlines(*args, **kwargs): +def vlines(x, ymin, ymax, colors='k', linestyles='solid', label='', hold=None, + **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.vlines(*args, **kwargs) + ret = ax.vlines(x, ymin, ymax, colors=colors, linestyles=linestyles, + label=label, **kwargs) finally: ax.hold(washold) @@ -3375,15 +3487,17 @@ def vlines(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.xcorr) -def xcorr(*args, **kwargs): +def xcorr(x, y, normed=True, detrend=mlab.detrend_none, usevlines=True, + maxlags=10, hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + if hold is not None: ax.hold(hold) try: - ret = ax.xcorr(*args, **kwargs) + ret = ax.xcorr(x, y, normed=normed, detrend=detrend, + usevlines=usevlines, maxlags=maxlags, **kwargs) finally: ax.hold(washold) @@ -3392,15 +3506,15 @@ def xcorr(*args, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.barbs) -def barbs(*args, **kwargs): +def barbs(*args, **kw): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() - hold = kwargs.pop('hold', None) + hold = kw.pop('hold', None) if hold is not None: ax.hold(hold) try: - ret = ax.barbs(*args, **kwargs) + ret = ax.barbs(*args, **kw) finally: ax.hold(washold) From bb4b9f79edb78ad328327f4d6ed8d76e977e7c0e Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 26 Aug 2015 21:57:54 -0400 Subject: [PATCH 38/60] MNT: add python 3 version of sig parsing --- lib/matplotlib/__init__.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 7997b35ede83..f5ce04b81933 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1587,13 +1587,23 @@ def foo(ax, *args, **kwargs) replace_names = set(replace_names) def param(func): - # this call is deprecated in 3.x and will be removed in py3.6 :-( - # TODO: implement the same for py 3.x/3.6+ with inspect.signature(..) - arg_spec = inspect.getargspec(func) - _arg_names = arg_spec.args - _has_no_varargs = arg_spec.varargs is None - _has_varkwargs = arg_spec.keywords is not None - + if six.PY2: + arg_spec = inspect.getargspec(func) + _arg_names = arg_spec.args + _has_no_varargs = arg_spec.varargs is None + _has_varkwargs = arg_spec.keywords is not None + elif six.PY3: + sig = inspect.signature(func) + _has_no_varargs = True + _has_varkwargs = False + _arg_names = [] + for p in sig.parameters.values(): + if p.kind is p.VAR_POSITIONAL: + _has_no_varargs = False + elif p.kind is p.VAR_KEYWORD: + _has_varkwargs = True + else: + _arg_names.append(p.name) # Import-time check: do we have enough information to replace *args? arg_names_at_runtime = False # there can't be any positional arguments behind *args and no From 9c1199b1b53a73f92f65e3be821194c4ea42250d Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 26 Aug 2015 22:18:41 -0400 Subject: [PATCH 39/60] ENH: add 'data' to signature in python 3.3 + - Properly check that inspect has the signature (keeps 3.0-3.2 nominally working) by checking version numbers --- lib/matplotlib/__init__.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index f5ce04b81933..8458a777ff38 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1587,23 +1587,35 @@ def foo(ax, *args, **kwargs) replace_names = set(replace_names) def param(func): - if six.PY2: + new_sig = None + ver_info = sys.version_info + _python_has_signature = ver_info.major > 2 and ver_info.minor > 2 + if not _python_has_signature: arg_spec = inspect.getargspec(func) _arg_names = arg_spec.args _has_no_varargs = arg_spec.varargs is None _has_varkwargs = arg_spec.keywords is not None - elif six.PY3: + else: sig = inspect.signature(func) _has_no_varargs = True _has_varkwargs = False _arg_names = [] - for p in sig.parameters.values(): + params = list(sig.parameters.values()) + for p in params: if p.kind is p.VAR_POSITIONAL: _has_no_varargs = False elif p.kind is p.VAR_KEYWORD: _has_varkwargs = True else: _arg_names.append(p.name) + data_param = inspect.Parameter('data', + inspect.Parameter.KEYWORD_ONLY, + default=None) + if _has_varkwargs: + params.insert(-1, data_param) + else: + params.append(data_param) + new_sig = sig.replace(parameters=params) # Import-time check: do we have enough information to replace *args? arg_names_at_runtime = False # there can't be any positional arguments behind *args and no @@ -1777,6 +1789,8 @@ def inner(ax, *args, **kwargs): _repl = _repl.format(names="', '".join(replace_names)) inner.__doc__ = (pre_doc + _DATA_DOC_APPENDIX.format(replaced=_repl)) + if new_sig is not None: + inner.__signature__ = new_sig return inner return param From 47ff38adc8e366cb6670d4acf1b456980e569f15 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 26 Aug 2015 22:36:54 -0400 Subject: [PATCH 40/60] MNT: python 2.6 compatibility fix --- lib/matplotlib/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 8458a777ff38..14bcc05984fb 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1589,7 +1589,9 @@ def foo(ax, *args, **kwargs) def param(func): new_sig = None ver_info = sys.version_info - _python_has_signature = ver_info.major > 2 and ver_info.minor > 2 + # py2.6 compatible version, use line below as soon as we can + _python_has_signature = ver_info[0] > 2 and ver_info[1] > 2 + # _python_has_signature = ver_info.major > 2 and ver_info.minor > 2 if not _python_has_signature: arg_spec = inspect.getargspec(func) _arg_names = arg_spec.args From 00929b9ad0c6996270ef7c1d65ac174c7f3e2eea Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 28 Aug 2015 00:09:35 -0400 Subject: [PATCH 41/60] MNT: yet more compatibility fixes In python > 3.2 `functools.update_wrapper` (which backs the `wraps` decorator) adds `__wrapped__` attribute to the returned function to provide easy access to the base function. Mock this up for legacy versions of python. --- lib/matplotlib/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 14bcc05984fb..093726bcecef 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1590,9 +1590,11 @@ def param(func): new_sig = None ver_info = sys.version_info # py2.6 compatible version, use line below as soon as we can - _python_has_signature = ver_info[0] > 2 and ver_info[1] > 2 - # _python_has_signature = ver_info.major > 2 and ver_info.minor > 2 - if not _python_has_signature: + python_has_signature = ver_info[0] > 2 and ver_info[1] > 2 + # python_has_signature = ver_info.major > 2 and ver_info.minor > 2 + python_has_wrapped = ver_info[0] > 2 and ver_info[1] > 1 + # python_has_wrapped = ver_info.major > 2 and ver_info.minor > 1 + if not python_has_signature: arg_spec = inspect.getargspec(func) _arg_names = arg_spec.args _has_no_varargs = arg_spec.varargs is None @@ -1791,6 +1793,8 @@ def inner(ax, *args, **kwargs): _repl = _repl.format(names="', '".join(replace_names)) inner.__doc__ = (pre_doc + _DATA_DOC_APPENDIX.format(replaced=_repl)) + if not python_has_wrapped: + inner.__wrapped__ = func if new_sig is not None: inner.__signature__ = new_sig return inner From b6cb12f760384a64eb2fabfbb3c03c6b98dd383c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 28 Aug 2015 00:34:20 -0400 Subject: [PATCH 42/60] ENH: add 'data' kwarg to pyplot --- boilerplate.py | 30 +++++++++- lib/matplotlib/pyplot.py | 118 +++++++++++++++++++++------------------ 2 files changed, 91 insertions(+), 57 deletions(-) diff --git a/boilerplate.py b/boilerplate.py index be2cd52d87e1..3e97b08a54fa 100644 --- a/boilerplate.py +++ b/boilerplate.py @@ -8,6 +8,7 @@ itself, whereas the generatable content must be edited via templates in this file. +This file is python 3 only due to the use of `inspect` """ # We did try to do the wrapping the smart way, # with callable functions and new.function, but could never get the @@ -209,9 +210,12 @@ def format_value(value): mappable = '' # Get argspec of wrapped function - work_func = getattr(Axes, func) - if hasattr(work_func, '__wrapped__'): - work_func = work_func.__wrapped__ + base_func = getattr(Axes, func) + has_data = 'data' in inspect.signature(base_func).parameters + if hasattr(base_func, '__wrapped__'): + work_func = base_func.__wrapped__ + else: + work_func = base_func args, varargs, varkw, defaults = inspect.getargspec(work_func) @@ -227,6 +231,15 @@ def format_value(value): def_edited.append(val) defaults = tuple(def_edited) + # Add a data keyword argument if needed (fmt is PLOT_TEMPLATE) and + # possible (if *args is used, we can't just add a data + # argument in front of it since it would gobble one of the + # arguments the user means to pass via *args) + # This needs to be done here so that it goes into call + if not varargs and fmt is PLOT_TEMPLATE and has_data: + args.append('data') + defaults = defaults + (None,) + # How to call the wrapped function call = [] for i, arg in enumerate(args): @@ -235,6 +248,14 @@ def format_value(value): else: call.append('%s=%s' % (arg, arg)) + # remove the data keyword as it was needed above to go into the + # call but should go after `hold` in the signature. + # This is janky as all get out, but hopefully boilerplate will + # be retired soon. + if not varargs and fmt is PLOT_TEMPLATE and has_data: + args.pop() + defaults = defaults[:-1] + if varargs is not None: call.append('*' + varargs) if varkw is not None: @@ -254,6 +275,9 @@ def format_value(value): elif fmt is PLOT_TEMPLATE: args.append('hold') defaults = defaults + (None,) + if has_data: + args.append('data') + defaults = defaults + (None,) sethold = '' # Now we can build the argspec for defining the wrapper diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index af52b1f8817e..a84e1cd90875 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -2508,7 +2508,7 @@ def spy(Z, precision=0, marker=None, markersize=None, aspect='equal', hold=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.acorr) -def acorr(x, hold=None, **kwargs): +def acorr(x, hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2516,7 +2516,7 @@ def acorr(x, hold=None, **kwargs): if hold is not None: ax.hold(hold) try: - ret = ax.acorr(x, **kwargs) + ret = ax.acorr(x, data=data, **kwargs) finally: ax.hold(washold) @@ -2526,7 +2526,7 @@ def acorr(x, hold=None, **kwargs): # changes will be lost @_autogen_docstring(Axes.angle_spectrum) def angle_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, - hold=None, **kwargs): + hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2535,7 +2535,7 @@ def angle_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, ax.hold(hold) try: ret = ax.angle_spectrum(x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, - sides=sides, **kwargs) + sides=sides, data=data, **kwargs) finally: ax.hold(washold) @@ -2629,7 +2629,7 @@ def axvspan(xmin, xmax, ymin=0, ymax=1, hold=None, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.bar) -def bar(left, height, width=0.8, bottom=None, hold=None, **kwargs): +def bar(left, height, width=0.8, bottom=None, hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2637,7 +2637,8 @@ def bar(left, height, width=0.8, bottom=None, hold=None, **kwargs): if hold is not None: ax.hold(hold) try: - ret = ax.bar(left, height, width=width, bottom=bottom, **kwargs) + ret = ax.bar(left, height, width=width, bottom=bottom, data=data, + **kwargs) finally: ax.hold(washold) @@ -2663,7 +2664,7 @@ def barh(bottom, width, height=0.8, left=None, hold=None, **kwargs): # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.broken_barh) -def broken_barh(xranges, yrange, hold=None, **kwargs): +def broken_barh(xranges, yrange, hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2671,7 +2672,7 @@ def broken_barh(xranges, yrange, hold=None, **kwargs): if hold is not None: ax.hold(hold) try: - ret = ax.broken_barh(xranges, yrange, **kwargs) + ret = ax.broken_barh(xranges, yrange, data=data, **kwargs) finally: ax.hold(washold) @@ -2685,7 +2686,7 @@ def boxplot(x, notch=None, sym=None, vert=None, whis=None, positions=None, conf_intervals=None, meanline=None, showmeans=None, showcaps=None, showbox=None, showfliers=None, boxprops=None, labels=None, flierprops=None, medianprops=None, meanprops=None, capprops=None, - whiskerprops=None, manage_xticks=True, hold=None): + whiskerprops=None, manage_xticks=True, hold=None, data=None): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2703,7 +2704,8 @@ def boxplot(x, notch=None, sym=None, vert=None, whis=None, positions=None, boxprops=boxprops, labels=labels, flierprops=flierprops, medianprops=medianprops, meanprops=meanprops, capprops=capprops, - whiskerprops=whiskerprops, manage_xticks=manage_xticks) + whiskerprops=whiskerprops, + manage_xticks=manage_xticks, data=data) finally: ax.hold(washold) @@ -2714,7 +2716,7 @@ def boxplot(x, notch=None, sym=None, vert=None, whis=None, positions=None, @_autogen_docstring(Axes.cohere) def cohere(x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, window=mlab.window_hanning, noverlap=0, pad_to=None, sides='default', - scale_by_freq=None, hold=None, **kwargs): + scale_by_freq=None, hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2724,7 +2726,8 @@ def cohere(x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, try: ret = ax.cohere(x, y, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, window=window, noverlap=noverlap, pad_to=pad_to, - sides=sides, scale_by_freq=scale_by_freq, **kwargs) + sides=sides, scale_by_freq=scale_by_freq, data=data, + **kwargs) finally: ax.hold(washold) @@ -2786,7 +2789,7 @@ def contourf(*args, **kwargs): @_autogen_docstring(Axes.csd) def csd(x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, sides=None, scale_by_freq=None, - return_line=None, hold=None, **kwargs): + return_line=None, hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2797,7 +2800,7 @@ def csd(x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, ret = ax.csd(x, y, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, window=window, noverlap=noverlap, pad_to=pad_to, sides=sides, scale_by_freq=scale_by_freq, - return_line=return_line, **kwargs) + return_line=return_line, data=data, **kwargs) finally: ax.hold(washold) @@ -2809,7 +2812,7 @@ def csd(x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, def errorbar(x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, capsize=None, barsabove=False, lolims=False, uplims=False, xlolims=False, xuplims=False, errorevery=1, capthick=None, - hold=None, **kwargs): + hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2821,7 +2824,8 @@ def errorbar(x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, elinewidth=elinewidth, capsize=capsize, barsabove=barsabove, lolims=lolims, uplims=uplims, xlolims=xlolims, xuplims=xuplims, - errorevery=errorevery, capthick=capthick, **kwargs) + errorevery=errorevery, capthick=capthick, data=data, + **kwargs) finally: ax.hold(washold) @@ -2832,7 +2836,7 @@ def errorbar(x, y, yerr=None, xerr=None, fmt='', ecolor=None, elinewidth=None, @_autogen_docstring(Axes.eventplot) def eventplot(positions, orientation='horizontal', lineoffsets=1, linelengths=1, linewidths=None, colors=None, linestyles='solid', hold=None, - **kwargs): + data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2843,7 +2847,7 @@ def eventplot(positions, orientation='horizontal', lineoffsets=1, linelengths=1, ret = ax.eventplot(positions, orientation=orientation, lineoffsets=lineoffsets, linelengths=linelengths, linewidths=linewidths, colors=colors, - linestyles=linestyles, **kwargs) + linestyles=linestyles, data=data, **kwargs) finally: ax.hold(washold) @@ -2870,7 +2874,7 @@ def fill(*args, **kwargs): # changes will be lost @_autogen_docstring(Axes.fill_between) def fill_between(x, y1, y2=0, where=None, interpolate=False, step=None, - hold=None, **kwargs): + hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2879,7 +2883,8 @@ def fill_between(x, y1, y2=0, where=None, interpolate=False, step=None, ax.hold(hold) try: ret = ax.fill_between(x, y1, y2=y2, where=where, - interpolate=interpolate, step=step, **kwargs) + interpolate=interpolate, step=step, data=data, + **kwargs) finally: ax.hold(washold) @@ -2888,7 +2893,8 @@ def fill_between(x, y1, y2=0, where=None, interpolate=False, step=None, # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost @_autogen_docstring(Axes.fill_betweenx) -def fill_betweenx(y, x1, x2=0, where=None, step=None, hold=None, **kwargs): +def fill_betweenx(y, x1, x2=0, where=None, step=None, hold=None, data=None, + **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2896,7 +2902,8 @@ def fill_betweenx(y, x1, x2=0, where=None, step=None, hold=None, **kwargs): if hold is not None: ax.hold(hold) try: - ret = ax.fill_betweenx(y, x1, x2=x2, where=where, step=step, **kwargs) + ret = ax.fill_betweenx(y, x1, x2=x2, where=where, step=step, data=data, + **kwargs) finally: ax.hold(washold) @@ -2909,7 +2916,7 @@ def hexbin(x, y, C=None, gridsize=100, bins=None, xscale='linear', yscale='linear', extent=None, cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, edgecolors='none', reduce_C_function=np.mean, mincnt=None, marginals=False, hold=None, - **kwargs): + data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2922,7 +2929,7 @@ def hexbin(x, y, C=None, gridsize=100, bins=None, xscale='linear', vmin=vmin, vmax=vmax, alpha=alpha, linewidths=linewidths, edgecolors=edgecolors, reduce_C_function=reduce_C_function, mincnt=mincnt, - marginals=marginals, **kwargs) + marginals=marginals, data=data, **kwargs) finally: ax.hold(washold) sci(ret) @@ -2934,7 +2941,7 @@ def hexbin(x, y, C=None, gridsize=100, bins=None, xscale='linear', def hist(x, bins=10, range=None, normed=False, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', orientation='vertical', rwidth=None, log=False, color=None, label=None, stacked=False, - hold=None, **kwargs): + hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2946,7 +2953,7 @@ def hist(x, bins=10, range=None, normed=False, weights=None, cumulative=False, weights=weights, cumulative=cumulative, bottom=bottom, histtype=histtype, align=align, orientation=orientation, rwidth=rwidth, log=log, color=color, label=label, - stacked=stacked, **kwargs) + stacked=stacked, data=data, **kwargs) finally: ax.hold(washold) @@ -2956,7 +2963,7 @@ def hist(x, bins=10, range=None, normed=False, weights=None, cumulative=False, # changes will be lost @_autogen_docstring(Axes.hist2d) def hist2d(x, y, bins=10, range=None, normed=False, weights=None, cmin=None, - cmax=None, hold=None, **kwargs): + cmax=None, hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2965,7 +2972,8 @@ def hist2d(x, y, bins=10, range=None, normed=False, weights=None, cmin=None, ax.hold(hold) try: ret = ax.hist2d(x, y, bins=bins, range=range, normed=normed, - weights=weights, cmin=cmin, cmax=cmax, **kwargs) + weights=weights, cmin=cmin, cmax=cmax, data=data, + **kwargs) finally: ax.hold(washold) sci(ret[-1]) @@ -2975,7 +2983,7 @@ def hist2d(x, y, bins=10, range=None, normed=False, weights=None, cmin=None, # changes will be lost @_autogen_docstring(Axes.hlines) def hlines(y, xmin, xmax, colors='k', linestyles='solid', label='', hold=None, - **kwargs): + data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -2984,7 +2992,7 @@ def hlines(y, xmin, xmax, colors='k', linestyles='solid', label='', hold=None, ax.hold(hold) try: ret = ax.hlines(y, xmin, xmax, colors=colors, linestyles=linestyles, - label=label, **kwargs) + label=label, data=data, **kwargs) finally: ax.hold(washold) @@ -2996,7 +3004,7 @@ def hlines(y, xmin, xmax, colors='k', linestyles='solid', label='', hold=None, def imshow(X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, vmin=None, vmax=None, origin=None, extent=None, shape=None, filternorm=1, filterrad=4.0, imlim=None, resample=None, url=None, - hold=None, **kwargs): + hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3008,7 +3016,8 @@ def imshow(X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, interpolation=interpolation, alpha=alpha, vmin=vmin, vmax=vmax, origin=origin, extent=extent, shape=shape, filternorm=filternorm, filterrad=filterrad, - imlim=imlim, resample=resample, url=url, **kwargs) + imlim=imlim, resample=resample, url=url, data=data, + **kwargs) finally: ax.hold(washold) sci(ret) @@ -3035,7 +3044,7 @@ def loglog(*args, **kwargs): # changes will be lost @_autogen_docstring(Axes.magnitude_spectrum) def magnitude_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, - sides=None, scale=None, hold=None, **kwargs): + sides=None, scale=None, hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3045,7 +3054,7 @@ def magnitude_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, try: ret = ax.magnitude_spectrum(x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, sides=sides, scale=scale, - **kwargs) + data=data, **kwargs) finally: ax.hold(washold) @@ -3089,7 +3098,7 @@ def pcolormesh(*args, **kwargs): # changes will be lost @_autogen_docstring(Axes.phase_spectrum) def phase_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, - hold=None, **kwargs): + hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3098,7 +3107,7 @@ def phase_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, ax.hold(hold) try: ret = ax.phase_spectrum(x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, - sides=sides, **kwargs) + sides=sides, data=data, **kwargs) finally: ax.hold(washold) @@ -3110,7 +3119,7 @@ def phase_spectrum(x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, def pie(x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=None, radius=None, counterclock=True, wedgeprops=None, textprops=None, - center=(0, 0), frame=False, hold=None): + center=(0, 0), frame=False, hold=None, data=None): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3123,7 +3132,7 @@ def pie(x, explode=None, labels=None, colors=None, autopct=None, labeldistance=labeldistance, startangle=startangle, radius=radius, counterclock=counterclock, wedgeprops=wedgeprops, textprops=textprops, center=center, - frame=frame) + frame=frame, data=data) finally: ax.hold(washold) @@ -3150,7 +3159,7 @@ def plot(*args, **kwargs): # changes will be lost @_autogen_docstring(Axes.plot_date) def plot_date(x, y, fmt='o', tz=None, xdate=True, ydate=False, hold=None, - **kwargs): + data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3159,7 +3168,7 @@ def plot_date(x, y, fmt='o', tz=None, xdate=True, ydate=False, hold=None, ax.hold(hold) try: ret = ax.plot_date(x, y, fmt=fmt, tz=tz, xdate=xdate, ydate=ydate, - **kwargs) + data=data, **kwargs) finally: ax.hold(washold) @@ -3170,7 +3179,7 @@ def plot_date(x, y, fmt='o', tz=None, xdate=True, ydate=False, hold=None, @_autogen_docstring(Axes.psd) def psd(x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, pad_to=None, sides=None, scale_by_freq=None, - return_line=None, hold=None, **kwargs): + return_line=None, hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3181,7 +3190,7 @@ def psd(x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, ret = ax.psd(x, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, window=window, noverlap=noverlap, pad_to=pad_to, sides=sides, scale_by_freq=scale_by_freq, - return_line=return_line, **kwargs) + return_line=return_line, data=data, **kwargs) finally: ax.hold(washold) @@ -3226,7 +3235,7 @@ def quiverkey(*args, **kw): @_autogen_docstring(Axes.scatter) def scatter(x, y, s=20, c=None, marker='o', cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=None, edgecolors=None, - hold=None, **kwargs): + hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3237,7 +3246,7 @@ def scatter(x, y, s=20, c=None, marker='o', cmap=None, norm=None, vmin=None, ret = ax.scatter(x, y, s=s, c=c, marker=marker, cmap=cmap, norm=norm, vmin=vmin, vmax=vmax, alpha=alpha, linewidths=linewidths, verts=verts, - edgecolors=edgecolors, **kwargs) + edgecolors=edgecolors, data=data, **kwargs) finally: ax.hold(washold) sci(ret) @@ -3283,7 +3292,7 @@ def semilogy(*args, **kwargs): def specgram(x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, cmap=None, xextent=None, pad_to=None, sides=None, scale_by_freq=None, mode=None, scale=None, vmin=None, vmax=None, - hold=None, **kwargs): + hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3295,7 +3304,7 @@ def specgram(x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, window=window, noverlap=noverlap, cmap=cmap, xextent=xextent, pad_to=pad_to, sides=sides, scale_by_freq=scale_by_freq, mode=mode, scale=scale, - vmin=vmin, vmax=vmax, **kwargs) + vmin=vmin, vmax=vmax, data=data, **kwargs) finally: ax.hold(washold) sci(ret[-1]) @@ -3357,7 +3366,7 @@ def step(x, y, *args, **kwargs): @_autogen_docstring(Axes.streamplot) def streamplot(x, y, u, v, density=1, linewidth=None, color=None, cmap=None, norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, - transform=None, zorder=1, start_points=None, hold=None): + transform=None, zorder=1, start_points=None, hold=None, data=None): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3369,7 +3378,7 @@ def streamplot(x, y, u, v, density=1, linewidth=None, color=None, cmap=None, color=color, cmap=cmap, norm=norm, arrowsize=arrowsize, arrowstyle=arrowstyle, minlength=minlength, transform=transform, - zorder=zorder, start_points=start_points) + zorder=zorder, start_points=start_points, data=data) finally: ax.hold(washold) sci(ret.lines) @@ -3448,7 +3457,7 @@ def triplot(*args, **kwargs): @_autogen_docstring(Axes.violinplot) def violinplot(dataset, positions=None, vert=True, widths=0.5, showmeans=False, showextrema=True, showmedians=False, points=100, bw_method=None, - hold=None): + hold=None, data=None): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3459,7 +3468,7 @@ def violinplot(dataset, positions=None, vert=True, widths=0.5, showmeans=False, ret = ax.violinplot(dataset, positions=positions, vert=vert, widths=widths, showmeans=showmeans, showextrema=showextrema, showmedians=showmedians, - points=points, bw_method=bw_method) + points=points, bw_method=bw_method, data=data) finally: ax.hold(washold) @@ -3469,7 +3478,7 @@ def violinplot(dataset, positions=None, vert=True, widths=0.5, showmeans=False, # changes will be lost @_autogen_docstring(Axes.vlines) def vlines(x, ymin, ymax, colors='k', linestyles='solid', label='', hold=None, - **kwargs): + data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3478,7 +3487,7 @@ def vlines(x, ymin, ymax, colors='k', linestyles='solid', label='', hold=None, ax.hold(hold) try: ret = ax.vlines(x, ymin, ymax, colors=colors, linestyles=linestyles, - label=label, **kwargs) + label=label, data=data, **kwargs) finally: ax.hold(washold) @@ -3488,7 +3497,7 @@ def vlines(x, ymin, ymax, colors='k', linestyles='solid', label='', hold=None, # changes will be lost @_autogen_docstring(Axes.xcorr) def xcorr(x, y, normed=True, detrend=mlab.detrend_none, usevlines=True, - maxlags=10, hold=None, **kwargs): + maxlags=10, hold=None, data=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() @@ -3497,7 +3506,8 @@ def xcorr(x, y, normed=True, detrend=mlab.detrend_none, usevlines=True, ax.hold(hold) try: ret = ax.xcorr(x, y, normed=normed, detrend=detrend, - usevlines=usevlines, maxlags=maxlags, **kwargs) + usevlines=usevlines, maxlags=maxlags, data=data, + **kwargs) finally: ax.hold(washold) From 25b1d43da08f411fa61a288077131456857af59e Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Fri, 28 Aug 2015 09:48:07 +0200 Subject: [PATCH 43/60] TST: test pyplot.pie with a data kwarg pie was a method, which did not work with a data kwarg as it didn't have a **kwargs catch all which would make pyplot.pie pass this argument to the decorated function. Document that the decorated functions are tested in the axes test cases. --- lib/matplotlib/tests/test_axes.py | 12 ++++++++++-- lib/matplotlib/tests/test_labeled_data_unpacking.py | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index d03d91020b23..e33d751984b0 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -3726,7 +3726,8 @@ def test_text_labelsize(): ax.tick_params(direction='out') -@image_comparison(baseline_images=['pie_linewidth_0', 'pie_linewidth_0'], +@image_comparison(baseline_images=['pie_linewidth_0', 'pie_linewidth_0', + 'pie_linewidth_0'], extensions=['png']) def test_pie_linewidth_0(): # The slices will be ordered and plotted counter-clockwise. @@ -3743,7 +3744,6 @@ def test_pie_linewidth_0(): # Reuse testcase from above for a labeled data test data = {"l": labels, "s": sizes, "c": colors, "ex": explode} - # FIXME: Workaround until the pyplot is regenerated fig = plt.figure() ax = fig.gca() ax.pie("s", explode="ex", labels="l", colors="c", @@ -3751,6 +3751,14 @@ def test_pie_linewidth_0(): wedgeprops={'linewidth': 0}, data=data) ax.axis('equal') + # And again to test the pyplot functions which should also be able to be + # called with a data kwarg + plt.figure() + plt.pie("s", explode="ex", labels="l", colors="c", + autopct='%1.1f%%', shadow=True, startangle=90, + wedgeprops={'linewidth': 0}, data=data) + plt.axis('equal') + @image_comparison(baseline_images=['pie_center_radius'], extensions=['png']) def test_pie_center_radius(): diff --git a/lib/matplotlib/tests/test_labeled_data_unpacking.py b/lib/matplotlib/tests/test_labeled_data_unpacking.py index 1f978fb3fefb..a596f78886fc 100644 --- a/lib/matplotlib/tests/test_labeled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labeled_data_unpacking.py @@ -30,6 +30,11 @@ def noop(txt, regex): def is_list_like(obj): return not is_string_like(obj) and iterable(obj) +# Notes on testing the plotting functions itself +# * the individual decorated plotting functions are tested in 'test_axes.py' +# * that pyplot functions accept a data kwarg is only tested in +# test_axes.test_pie_linewidth_0 + # stolen from pandas @contextmanager From b4f011c570fadc99e50e1502df2cf8fd4fbe40a1 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Fri, 28 Aug 2015 10:19:21 +0200 Subject: [PATCH 44/60] ENH: remove arbitrary long args if used with data Arbitrary long args, i.e., plot("x","y","r","x2","y2","b") were problematic if used with a data kwarg and the color spec ("r", "b") was included in data: this made it impossible in some cases to determine what the user wanted to plot: plot("x","y","r","x2","y2","b", data={..., "r":..., "b":...) could be interpreted as plot(x, y, "r") # all points red plot(x2, y2, "b") # all points black or plot(x,y) plot(r,x2) plot(y2,b) This could lead to hard to debug problems if both styles result in a similar plot (e.g. if all are values in the same range). Therefore it was decided to remove this possibility so that the usere gets a proper error message instead: https://github.com/matplotlib/matplotlib/pull/4829#issuecomment-135514572 There is still a case of ambiguity (plot("y", "ro", data={"y":..., "ro":...), which is now detected and a warning is issued. This detection could theoretically be used to detect the above case as well, but there were so many corner cases, that the checks became too horrible and I took that out again. Note that passing in data directly (without a data kwarg) is unaffected, it still accepts arbitrary long args. --- lib/matplotlib/axes/_axes.py | 74 +++++++++++-------- .../tests/test_labeled_data_unpacking.py | 28 +++---- 2 files changed, 55 insertions(+), 47 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 11cf0f79c053..facd52f3ed67 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -49,42 +49,48 @@ def _plot_args_replacer(args, data): - _replacer = [] - remaining = args - while 1: - if len(remaining) == 1: - - msg = ("Missing argument: this can happen if a color spec " - "('c') is in `data`") - warnings.warn(msg, RuntimeWarning, stacklevel=3) - _replacer += ["x"] - elif len(remaining) == 2: - _replacer += ["x", "y"] - elif len(remaining) == 3: - if remaining[2] in data: + if len(args) == 1: + return ["y"] + elif len(args) == 2: + # this can be two cases: x,y or y,c + if not args[1] in data: + # this is not in data, so just assume that it is something which + # will not get replaced (color spec or array like). + return ["y", "c"] + # it's data, but could be a color code like 'ro' or 'b--' + # -> warn the user in that case... + arg2 = args[1] + if is_string_like(arg2) and len(arg2) <= 3: + # all possible linestyles and color codes -> see doc of plot + reserved_ls = ["-", "--", "-.", ":", ".", ",", "o", "v", "^", "<", + ">", "1", "2", "3", "4", "s", "p", "*", "h", "H", + "+", "x", "D", "d", "|", "_"] + reserved_cc = ["b", "r", "c", "m", "y", "k", "w"] + # remove the line style part + for ls in reserved_ls: + if ls in arg2: + arg2 = arg2.replace(ls, '') + continue + # can now only be a color code... + if arg2 in reserved_cc: import warnings - - msg = "Found a color spec ('c') in data." + msg = "Second argument is ambiguous: could be a color spec " \ + "but is in data. Using as data.\nEither rename the " \ + "entry in data or use three arguments to plot." warnings.warn(msg, RuntimeWarning, stacklevel=3) - _replacer += ["x", "y", "c"] - - # if less than 3, the above code handled it so just return - if len(remaining) <= 3: - return _replacer - - # More than 3 -> split off the beginning and continue - if remaining[2] not in data: - _replacer += ["x", "y", "c"] - isplit = 3 - else: - _replacer += ["x", "y"] - isplit = 2 - remaining = remaining[isplit:] + return ["x", "y"] + elif len(args) == 3: + return ["x", "y", "c"] + else: + raise ValueError("Using arbitrary long args with data is not " + "supported due to ambiguity of arguments.\nUse " + "multiple plotting calls instead.") # The axes module contains all the wrappers to plotting functions. # All the other methods should go in the _AxesBase class. + class Axes(_AxesBase): """ The :class:`Axes` contains most of the figure elements: @@ -1305,8 +1311,14 @@ def plot(self, *args, **kwargs): If *x* and/or *y* is 2-dimensional, then the corresponding columns will be plotted. - An arbitrary number of *x*, *y*, *fmt* groups can be - specified, as in:: + If used with labeled data, make sure that the color spec is not + included as an element in data, as otherwise the last case + ``plot("v","r", data={"v":..., "r":...)`` + can be interpreted as the first case which would do ``plot(v, r)`` + using the default line style and color. + + If not used with labeled data (i.e., without a data argument), + an arbitrary number of *x*, *y*, *fmt* groups can be specified, as in:: a.plot(x1, y1, 'g^', x2, y2, 'g-') diff --git a/lib/matplotlib/tests/test_labeled_data_unpacking.py b/lib/matplotlib/tests/test_labeled_data_unpacking.py index a596f78886fc..f52d671fdcbd 100644 --- a/lib/matplotlib/tests/test_labeled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labeled_data_unpacking.py @@ -467,31 +467,27 @@ def test_positional_parameter_names_as_function(): # Also test the _plot_arg_replacer for plot... from matplotlib.axes._axes import _plot_args_replacer - # this is a replace for plot, which can take args as - # x,y,c,x,y,c or x,y,x,y,c,x,y or any other way... :-/ @unpack_labeled_data(replace_names=["x", "y"], positional_parameter_names=_plot_args_replacer) def funcy(ax, *args, **kwargs): return "{args} | {kwargs}".format(args=args, kwargs=kwargs) + # the normal case... data = {"x": "X", "y": "Y"} assert_equal(funcy(None, "x", "y", data=data), "('X', 'Y') | {}") assert_equal(funcy(None, "x", "y", "c", data=data), "('X', 'Y', 'c') | {}") - assert_equal(funcy(None, "x", "y", "c", "x", "y", "x", "y", data=data), - "('X', 'Y', 'c', 'X', 'Y', 'X', 'Y') | {}") - # the color spec should not be in data... - data = {"x": "X", "y": "Y", "c": "!!"} - with assert_produces_warning(RuntimeWarning): + # no arbitrary long args with data + def f(): assert_equal(funcy(None, "x", "y", "c", "x", "y", "x", "y", data=data), - "('X', 'Y', '!!', 'X', 'Y', 'X', 'y') | {}") - - # And this is the case which we screw up, as we can't distinguish - # between a color spec and data... - # -> This test should actually produce a warning, if someone finds a way - # to distinguish between data and color spec... - with assert_produces_warning(False): - assert_equal(funcy(None, "x", "y", "c", "x", "y", "c", data=data), - "('X', 'Y', '!!', 'X', 'Y', '!!') | {}") + "('X', 'Y', 'c', 'X', 'Y', 'X', 'Y') | {}") + assert_raises(ValueError, f) + + # In the two arg case, if a valid color spec is in data, we warn but use + # it as data... + data = {"x": "X", "y": "Y", "ro": "!!"} + with assert_produces_warning(RuntimeWarning): + assert_equal(funcy(None, "y", "ro", data=data), + "('Y', '!!') | {}") From 61eb622a6989de08cf3f69ea536425cd0c19e4ef Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Mon, 31 Aug 2015 09:28:35 +0200 Subject: [PATCH 45/60] FIX: small (non-code) fixups --- lib/matplotlib/__init__.py | 1 - lib/matplotlib/tests/test_labeled_data_unpacking.py | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 093726bcecef..fd6bd2a5cf2e 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1775,7 +1775,6 @@ def inner(ax, *args, **kwargs): "the matplotlib list!)") warnings.warn(msg % (label_namer, func.__name__), RuntimeWarning, stacklevel=2) - # raise Exception() return func(ax, *args, **kwargs) pre_doc = inner.__doc__ if pre_doc is None: diff --git a/lib/matplotlib/tests/test_labeled_data_unpacking.py b/lib/matplotlib/tests/test_labeled_data_unpacking.py index f52d671fdcbd..4a3f6e13355d 100644 --- a/lib/matplotlib/tests/test_labeled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labeled_data_unpacking.py @@ -343,7 +343,7 @@ def func_varags_replace_all(ax, *args, **kwargs): def test_no_label_replacements(): - """Test without "label_namer=None" -> no label replacement at all""" + """Test with "label_namer=None" -> no label replacement at all""" @unpack_labeled_data(replace_names=["x", "y"], label_namer=None) def func_no_label(ax, x, y, ls="x", label=None, w="xyz"): @@ -390,11 +390,8 @@ def funcy(ax, *args, **kwargs): list(x), list(y), ls, w, label) func = unpack_labeled_data(replace_all_args=True, replace_names=["w"], - label_namer="y")( - funcy) + label_namer="y")(funcy) - # assert_equal(func(None, "a","b", w="x", data=data), - # "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: None") assert_equal(func(None, "a", "b", w="x", label="", data=data), "x: [1, 2], y: [8, 9], ls: x, w: xyz, label: ") assert_equal(func(None, "a", "b", w="x", label="text", data=data), From e838a25d8a401e20992eaa5a901c767b1b5232f4 Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Mon, 31 Aug 2015 09:36:16 +0200 Subject: [PATCH 46/60] MNT: put test helper functions into their proper place --- lib/matplotlib/cbook.py | 3 + lib/matplotlib/testing/__init__.py | 66 +++++++++++++++++ .../tests/test_labeled_data_unpacking.py | 70 +------------------ 3 files changed, 70 insertions(+), 69 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index d2409396ce3d..6ba700ff6bbe 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -708,6 +708,9 @@ def is_string_like(obj): return False return True +def is_list_like(obj): + """Returns whether the obj is iterable and not a string""" + return not is_string_like(obj) and iterable(obj) def is_sequence_of_strings(obj): """ diff --git a/lib/matplotlib/testing/__init__.py b/lib/matplotlib/testing/__init__.py index 800d82e7ee00..85215da4cace 100644 --- a/lib/matplotlib/testing/__init__.py +++ b/lib/matplotlib/testing/__init__.py @@ -1,2 +1,68 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) + + +from contextlib import contextmanager + + +# stolen from pandas +@contextmanager +def assert_produces_warning(expected_warning=Warning, filter_level="always", + clear=None): + """ + Context manager for running code that expects to raise (or not raise) + warnings. Checks that code raises the expected warning and only the + expected warning. Pass ``False`` or ``None`` to check that it does *not* + raise a warning. Defaults to ``exception.Warning``, baseclass of all + Warnings. (basically a wrapper around ``warnings.catch_warnings``). + + >>> import warnings + >>> with assert_produces_warning(): + ... warnings.warn(UserWarning()) + ... + >>> with assert_produces_warning(False): + ... warnings.warn(RuntimeWarning()) + ... + Traceback (most recent call last): + ... + AssertionError: Caused unexpected warning(s): ['RuntimeWarning']. + >>> with assert_produces_warning(UserWarning): + ... warnings.warn(RuntimeWarning()) + Traceback (most recent call last): + ... + AssertionError: Did not see expected warning of class 'UserWarning'. + + ..warn:: This is *not* thread-safe. + """ + import warnings + from matplotlib.cbook import is_list_like + + with warnings.catch_warnings(record=True) as w: + + if clear is not None: + # make sure that we are clearning these warnings + # if they have happened before + # to guarantee that we will catch them + if not is_list_like(clear): + clear = [clear] + for m in clear: + try: + m.__warningregistry__.clear() + except: + pass + + saw_warning = False + warnings.simplefilter(filter_level) + yield w + extra_warnings = [] + for actual_warning in w: + if (expected_warning and issubclass(actual_warning.category, + expected_warning)): + saw_warning = True + else: + extra_warnings.append(actual_warning.category.__name__) + if expected_warning: + assert saw_warning, ("Did not see expected warning of class %r." + % expected_warning.__name__) + assert not extra_warnings, ("Caused unexpected warning(s): %r." + % extra_warnings) diff --git a/lib/matplotlib/tests/test_labeled_data_unpacking.py b/lib/matplotlib/tests/test_labeled_data_unpacking.py index 4a3f6e13355d..7ce93b677c3a 100644 --- a/lib/matplotlib/tests/test_labeled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labeled_data_unpacking.py @@ -19,85 +19,17 @@ def noop(txt, regex): assert_regex = noop assert_not_regex = noop - -from matplotlib.cbook import is_string_like, iterable - -from contextlib import contextmanager +from ..testing import assert_produces_warning from .. import unpack_labeled_data -def is_list_like(obj): - return not is_string_like(obj) and iterable(obj) - # Notes on testing the plotting functions itself # * the individual decorated plotting functions are tested in 'test_axes.py' # * that pyplot functions accept a data kwarg is only tested in # test_axes.test_pie_linewidth_0 -# stolen from pandas -@contextmanager -def assert_produces_warning(expected_warning=Warning, filter_level="always", - clear=None): - """ - Context manager for running code that expects to raise (or not raise) - warnings. Checks that code raises the expected warning and only the - expected warning. Pass ``False`` or ``None`` to check that it does *not* - raise a warning. Defaults to ``exception.Warning``, baseclass of all - Warnings. (basically a wrapper around ``warnings.catch_warnings``). - - >>> import warnings - >>> with assert_produces_warning(): - ... warnings.warn(UserWarning()) - ... - >>> with assert_produces_warning(False): - ... warnings.warn(RuntimeWarning()) - ... - Traceback (most recent call last): - ... - AssertionError: Caused unexpected warning(s): ['RuntimeWarning']. - >>> with assert_produces_warning(UserWarning): - ... warnings.warn(RuntimeWarning()) - Traceback (most recent call last): - ... - AssertionError: Did not see expected warning of class 'UserWarning'. - - ..warn:: This is *not* thread-safe. - """ - import warnings - - with warnings.catch_warnings(record=True) as w: - - if clear is not None: - # make sure that we are clearning these warnings - # if they have happened before - # to guarantee that we will catch them - if not is_list_like(clear): - clear = [clear] - for m in clear: - try: - m.__warningregistry__.clear() - except: - pass - - saw_warning = False - warnings.simplefilter(filter_level) - yield w - extra_warnings = [] - for actual_warning in w: - if (expected_warning and issubclass(actual_warning.category, - expected_warning)): - saw_warning = True - else: - extra_warnings.append(actual_warning.category.__name__) - if expected_warning: - assert saw_warning, ("Did not see expected warning of class %r." - % expected_warning.__name__) - assert not extra_warnings, ("Caused unexpected warning(s): %r." - % extra_warnings) - - # these two get used in multiple tests, so define them here @unpack_labeled_data(replace_names=["x", "y"], label_namer="y") def plot_func(ax, x, y, ls="x", label=None, w="xyz"): From 55ed86e108129195b11fbfa277b29f8c595bbeaa Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Mon, 31 Aug 2015 09:47:01 +0200 Subject: [PATCH 47/60] TST: Test with pandas installed on 2.7 Also make a unit test more specific, as it triggered a warning which wasn't intended in that case. The warning is tested a few lines later... --- .travis.yml | 5 +++-- lib/matplotlib/tests/test_labeled_data_unpacking.py | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2c9279bd83fb..9dc579d4fb31 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ env: - secure: "dfjNqGKzQG5bu3FnDNwLG8H/C4QoieFo4PfFmZPdM2RY7WIzukwKFNT6kiDfOrpwt+2bR7FhzjOGlDECGtlGOtYPN8XuXGjhcP4a4IfakdbDfF+D3NPIpf5VlE6776k0VpvcZBTMYJKNFIMc7QPkOwjvNJ2aXyfe3hBuGlKJzQU=" - BUILD_DOCS=false - NUMPY=numpy + - PANDAS= - NPROC=2 - TEST_ARGS=--no-pep8 @@ -36,7 +37,7 @@ matrix: - python: 2.6 env: NUMPY=numpy==1.6 MOCK=mock==1.0.1 - python: 2.7 - env: MOCK=mock + env: MOCK=mock PANDAS=pandas - python: 3.3 - python: 3.4 - python: 2.7 @@ -62,7 +63,7 @@ install: pip install --upgrade setuptools # Install only from travis wheelhouse - if [ -z "$PRE" ]; then - wheelhouse_pip_install python-dateutil $NUMPY pyparsing pillow sphinx!=1.3.0; + wheelhouse_pip_install python-dateutil $NUMPY $PANDAS pyparsing pillow sphinx!=1.3.0; else pip install $PRE python-dateutil $NUMPY pyparsing pillow sphinx!=1.3.0; fi diff --git a/lib/matplotlib/tests/test_labeled_data_unpacking.py b/lib/matplotlib/tests/test_labeled_data_unpacking.py index 7ce93b677c3a..e8baf8235e4f 100644 --- a/lib/matplotlib/tests/test_labeled_data_unpacking.py +++ b/lib/matplotlib/tests/test_labeled_data_unpacking.py @@ -402,10 +402,10 @@ def funcy(ax, *args, **kwargs): return "{args} | {kwargs}".format(args=args, kwargs=kwargs) # the normal case... - data = {"x": "X", "y": "Y"} - assert_equal(funcy(None, "x", "y", data=data), + data = {"x": "X", "y1": "Y"} + assert_equal(funcy(None, "x", "y1", data=data), "('X', 'Y') | {}") - assert_equal(funcy(None, "x", "y", "c", data=data), + assert_equal(funcy(None, "x", "y1", "c", data=data), "('X', 'Y', 'c') | {}") # no arbitrary long args with data From 9846b9c6205b2abb902690195c0e05862c0ddf6e Mon Sep 17 00:00:00 2001 From: Jan Schulz Date: Mon, 31 Aug 2015 19:29:30 +0200 Subject: [PATCH 48/60] DOC: Add a whatsnew entry for labeled data improvements --- .../2015-07-30_unpack_labeled_data.rst | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 doc/users/whats_new/2015-07-30_unpack_labeled_data.rst diff --git a/doc/users/whats_new/2015-07-30_unpack_labeled_data.rst b/doc/users/whats_new/2015-07-30_unpack_labeled_data.rst new file mode 100644 index 000000000000..5998417cec0d --- /dev/null +++ b/doc/users/whats_new/2015-07-30_unpack_labeled_data.rst @@ -0,0 +1,30 @@ +Working with labeled data like pandas DataFrames +------------------------------------------------ +Plot methods which take arrays as inputs can now also work with labeled data +and unpack such data. + +This means that the following two examples produce the same plot:: + +Example :: + df = pandas.DataFrame({"var1":[1,2,3,4,5,6], "var2":[1,2,3,4,5,6]}) + plt.plot(df["var1"], df["var2"]) + + +Example :: + plt.plot("var1", "var2", data=df) + +This works for most plotting methods, which expect arrays/sequences as +inputs and ``data`` can be anything which supports ``__get_item__`` +(``dict``, ``pandas.DataFrame``,...). + +In addition to this, some other changes were made, which makes working with +``pandas.DataFrames`` easier: + +* For plotting methods which understand a ``label`` keyword argument but the + user does not supply such an argument, this is now implicitly set by either + looking up ``.name`` of the right input or by using the label supplied to + lookup the input in ``data``. In the above examples, this results in an + implicit ``label="var2"`` for both cases. + +* ``plot()`` now uses the index of a ``Series`` instead of + ``np.arange(len(y))``, if no ``x`` argument is supplied. From 2b2092d902cab574fe81d9428d882a38df784066 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 8 Sep 2015 12:31:44 -0400 Subject: [PATCH 49/60] MNT: use IPython's signature if needed + available IPython ships a version of `Signature` and friends that runs on python >=2.7. Fall back to this version of things for python <=3.2. --- lib/matplotlib/__init__.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index fd6bd2a5cf2e..c6e28ffd6ad0 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1594,27 +1594,40 @@ def param(func): # python_has_signature = ver_info.major > 2 and ver_info.minor > 2 python_has_wrapped = ver_info[0] > 2 and ver_info[1] > 1 # python_has_wrapped = ver_info.major > 2 and ver_info.minor > 1 + if not python_has_signature: + try: + import IPython.utils.signatures + signature = IPython.utils.signatures.signature + Parameter = IPython.utils.signatures.Parameter + except ImportError: + pass + else: + python_has_signature = True + else: + signature = inspect.signature + Parameter = inspect.Parameter + if not python_has_signature: arg_spec = inspect.getargspec(func) _arg_names = arg_spec.args _has_no_varargs = arg_spec.varargs is None _has_varkwargs = arg_spec.keywords is not None else: - sig = inspect.signature(func) + sig = signature(func) _has_no_varargs = True _has_varkwargs = False _arg_names = [] params = list(sig.parameters.values()) for p in params: - if p.kind is p.VAR_POSITIONAL: + if p.kind is Parameter.VAR_POSITIONAL: _has_no_varargs = False - elif p.kind is p.VAR_KEYWORD: + elif p.kind is Parameter.VAR_KEYWORD: _has_varkwargs = True else: _arg_names.append(p.name) - data_param = inspect.Parameter('data', - inspect.Parameter.KEYWORD_ONLY, - default=None) + data_param = Parameter('data', + Parameter.KEYWORD_ONLY, + default=None) if _has_varkwargs: params.insert(-1, data_param) else: From e2cf26468f7b36e4ed1e94a02406ef537ac2691c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 9 Sep 2015 18:51:00 -0400 Subject: [PATCH 50/60] MNT: use already imported version variables --- lib/matplotlib/__init__.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index c6e28ffd6ad0..b5784c89898c 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1588,12 +1588,9 @@ def foo(ax, *args, **kwargs) def param(func): new_sig = None - ver_info = sys.version_info - # py2.6 compatible version, use line below as soon as we can - python_has_signature = ver_info[0] > 2 and ver_info[1] > 2 - # python_has_signature = ver_info.major > 2 and ver_info.minor > 2 - python_has_wrapped = ver_info[0] > 2 and ver_info[1] > 1 - # python_has_wrapped = ver_info.major > 2 and ver_info.minor > 1 + python_has_signature = major >= 3 and minor1 >= 3 + python_has_wrapped = major >= 3 and minor1 >= 2 + if not python_has_signature: try: import IPython.utils.signatures From 67fda44c233adc486cd98ec4ff7ff4b942f96cd8 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 9 Sep 2015 18:51:27 -0400 Subject: [PATCH 51/60] PRF: only try to use IPython if already imported --- lib/matplotlib/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index b5784c89898c..13585ea1c516 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1591,7 +1591,9 @@ def param(func): python_has_signature = major >= 3 and minor1 >= 3 python_has_wrapped = major >= 3 and minor1 >= 2 - if not python_has_signature: + # if in a legacy version of python and IPython is already imported + # try to use their back-ported signature + if not python_has_signature and 'IPython' in sys.modules: try: import IPython.utils.signatures signature = IPython.utils.signatures.signature From 1c9feb824837af2945c5198b7c6d43e501cd595f Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 10 Sep 2015 23:32:32 -0400 Subject: [PATCH 52/60] FIX: guard signature import better --- lib/matplotlib/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 13585ea1c516..67632873561d 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1603,8 +1603,9 @@ def param(func): else: python_has_signature = True else: - signature = inspect.signature - Parameter = inspect.Parameter + if python_has_signature: + signature = inspect.signature + Parameter = inspect.Parameter if not python_has_signature: arg_spec = inspect.getargspec(func) From 1d00767907298bebb569f0851bf3b99a58682147 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 10 Sep 2015 23:36:48 -0400 Subject: [PATCH 53/60] MNT: use built-in logic for unwrapping --- boilerplate.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/boilerplate.py b/boilerplate.py index 3e97b08a54fa..67fb7001146e 100644 --- a/boilerplate.py +++ b/boilerplate.py @@ -212,10 +212,7 @@ def format_value(value): # Get argspec of wrapped function base_func = getattr(Axes, func) has_data = 'data' in inspect.signature(base_func).parameters - if hasattr(base_func, '__wrapped__'): - work_func = base_func.__wrapped__ - else: - work_func = base_func + work_func = inspect.unwrap(base_func) args, varargs, varkw, defaults = inspect.getargspec(work_func) From c45498395c07be6d27f9e96769732464dbe1b852 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 10 Sep 2015 23:43:39 -0400 Subject: [PATCH 54/60] MNT: rename get_index_y -> index_of --- lib/matplotlib/axes/_base.py | 6 +++--- lib/matplotlib/cbook.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index f7e64aba22ab..c1ff5ddc0427 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -15,7 +15,7 @@ import matplotlib from matplotlib import cbook -from matplotlib.cbook import _string_to_bool, iterable, get_index_y, get_label +from matplotlib.cbook import _string_to_bool, iterable, index_of, get_label from matplotlib import docstring import matplotlib.colors as mcolors import matplotlib.lines as mlines @@ -31,7 +31,7 @@ import matplotlib.image as mimage from matplotlib.offsetbox import OffsetBox from matplotlib.artist import allow_rasterization -from matplotlib.cbook import iterable, get_index_y +from matplotlib.cbook import iterable, index_of from matplotlib.rcsetup import cycler rcParams = matplotlib.rcParams @@ -356,7 +356,7 @@ def _plot_args(self, tup, kwargs): x = np.atleast_1d(tup[0]) y = np.atleast_1d(tup[-1]) else: - x, y = get_index_y(tup[-1]) + x, y = index_of(tup[-1]) x, y = self._xy_from_xy(x, y) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 6ba700ff6bbe..41a28a6f4516 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2491,7 +2491,7 @@ def pts_to_midstep(x, *args): 'step-mid': pts_to_midstep} -def get_index_y(y): +def index_of(y): """ A helper function to get the index of an input to plot against if x values are not explicitly given. From 0d6cd401384f476aac06aec6f559a9f956057786 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 10 Sep 2015 23:47:38 -0400 Subject: [PATCH 55/60] MNT: enable unpacking on 2D inputs --- lib/matplotlib/axes/_axes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index facd52f3ed67..bc45a058f2ed 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -5641,7 +5641,7 @@ def pcolorfast(self, *args, **kwargs): self.autoscale_view(tight=True) return ret - #@unpack_labeled_data() # takes 2d data :-( + @unpack_labeled_data() def contour(self, *args, **kwargs): if not self._hold: self.cla() @@ -5649,7 +5649,7 @@ def contour(self, *args, **kwargs): return mcontour.QuadContourSet(self, *args, **kwargs) contour.__doc__ = mcontour.QuadContourSet.contour_doc - #@unpack_labeled_data() # takes 2d data :-( + @unpack_labeled_data() def contourf(self, *args, **kwargs): if not self._hold: self.cla() From 0734b86143f50b5bba8b049fe7072a1877cca179 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 10 Sep 2015 23:53:09 -0400 Subject: [PATCH 56/60] MNT: invert logic of _has_varargs --- lib/matplotlib/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 67632873561d..281c7ec83dfe 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1610,17 +1610,17 @@ def param(func): if not python_has_signature: arg_spec = inspect.getargspec(func) _arg_names = arg_spec.args - _has_no_varargs = arg_spec.varargs is None + _has_varargs = arg_spec.varargs is not None _has_varkwargs = arg_spec.keywords is not None else: sig = signature(func) - _has_no_varargs = True + _has_varargs = False _has_varkwargs = False _arg_names = [] params = list(sig.parameters.values()) for p in params: if p.kind is Parameter.VAR_POSITIONAL: - _has_no_varargs = False + _has_varargs = True elif p.kind is Parameter.VAR_KEYWORD: _has_varkwargs = True else: @@ -1639,7 +1639,7 @@ def param(func): # positional args can end up in **kwargs, so only *varargs make # problems. # http://stupidpythonideas.blogspot.de/2013/08/arguments-and-parameters.html - if _has_no_varargs: + if not _has_varargs: # all args are "named", so no problem # remove the first "ax" / self arg arg_names = _arg_names[1:] From f34aed0b9a936f89d857fb49f4ecc67d16b44ce1 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 12 Sep 2015 16:35:49 -0400 Subject: [PATCH 57/60] MNT: simplify identifying valid style codes If the second arguement to `plot` is both in data and a valid style code warn the user. --- lib/matplotlib/axes/_axes.py | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index bc45a058f2ed..f39ee374a745 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -59,25 +59,15 @@ def _plot_args_replacer(args, data): return ["y", "c"] # it's data, but could be a color code like 'ro' or 'b--' # -> warn the user in that case... - arg2 = args[1] - if is_string_like(arg2) and len(arg2) <= 3: - # all possible linestyles and color codes -> see doc of plot - reserved_ls = ["-", "--", "-.", ":", ".", ",", "o", "v", "^", "<", - ">", "1", "2", "3", "4", "s", "p", "*", "h", "H", - "+", "x", "D", "d", "|", "_"] - reserved_cc = ["b", "r", "c", "m", "y", "k", "w"] - # remove the line style part - for ls in reserved_ls: - if ls in arg2: - arg2 = arg2.replace(ls, '') - continue - # can now only be a color code... - if arg2 in reserved_cc: - import warnings - msg = "Second argument is ambiguous: could be a color spec " \ - "but is in data. Using as data.\nEither rename the " \ - "entry in data or use three arguments to plot." - warnings.warn(msg, RuntimeWarning, stacklevel=3) + try: + _process_plot_format(args[1]) + except ValueError: + pass + else: + msg = "Second argument is ambiguous: could be a color spec " \ + "but is in data. Using as data.\nEither rename the " \ + "entry in data or use three arguments to plot." + warnings.warn(msg, RuntimeWarning, stacklevel=3) return ["x", "y"] elif len(args) == 3: return ["x", "y", "c"] From a051b572cc994ad72a87b0ecda5e95d7dddb3125 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 12 Sep 2015 16:56:30 -0400 Subject: [PATCH 58/60] MNT: rearrange testing helper functions --- lib/matplotlib/cbook.py | 3 --- lib/matplotlib/testing/__init__.py | 14 +++++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 41a28a6f4516..506e34a4fcd2 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -708,9 +708,6 @@ def is_string_like(obj): return False return True -def is_list_like(obj): - """Returns whether the obj is iterable and not a string""" - return not is_string_like(obj) and iterable(obj) def is_sequence_of_strings(obj): """ diff --git a/lib/matplotlib/testing/__init__.py b/lib/matplotlib/testing/__init__.py index 85215da4cace..6addb0e53f76 100644 --- a/lib/matplotlib/testing/__init__.py +++ b/lib/matplotlib/testing/__init__.py @@ -1,9 +1,16 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) - +import warnings from contextlib import contextmanager +from matplotlib.cbook import is_string_like, iterable + + +def _is_list_like(obj): + """Returns whether the obj is iterable and not a string""" + return not is_string_like(obj) and iterable(obj) + # stolen from pandas @contextmanager @@ -34,16 +41,13 @@ def assert_produces_warning(expected_warning=Warning, filter_level="always", ..warn:: This is *not* thread-safe. """ - import warnings - from matplotlib.cbook import is_list_like - with warnings.catch_warnings(record=True) as w: if clear is not None: # make sure that we are clearning these warnings # if they have happened before # to guarantee that we will catch them - if not is_list_like(clear): + if not _is_list_like(clear): clear = [clear] for m in clear: try: From 82719525ee5e213fa6c42c1d800bf97b02121f6f Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 12 Sep 2015 17:55:31 -0400 Subject: [PATCH 59/60] MNT: expose hist(..., weight='key') to data kwarg --- lib/matplotlib/axes/_axes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index f39ee374a745..67acaae161a5 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -5680,7 +5680,7 @@ def table(self, **kwargs): #### Data analysis - @unpack_labeled_data(replace_names=["x"], label_namer="x") + @unpack_labeled_data(replace_names=["x", 'weights'], label_namer="x") @docstring.dedent_interpd def hist(self, x, bins=10, range=None, normed=False, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', From 0b4fc7cf17087ba6971b8e44f8cb0076c3afd623 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 12 Sep 2015 18:20:25 -0400 Subject: [PATCH 60/60] DOC: edits to whats_new entry --- .../2015-07-30_unpack_labeled_data.rst | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/doc/users/whats_new/2015-07-30_unpack_labeled_data.rst b/doc/users/whats_new/2015-07-30_unpack_labeled_data.rst index 5998417cec0d..04d5e5985563 100644 --- a/doc/users/whats_new/2015-07-30_unpack_labeled_data.rst +++ b/doc/users/whats_new/2015-07-30_unpack_labeled_data.rst @@ -3,28 +3,34 @@ Working with labeled data like pandas DataFrames Plot methods which take arrays as inputs can now also work with labeled data and unpack such data. -This means that the following two examples produce the same plot:: +This means that the following two examples produce the same plot: Example :: + df = pandas.DataFrame({"var1":[1,2,3,4,5,6], "var2":[1,2,3,4,5,6]}) plt.plot(df["var1"], df["var2"]) Example :: + plt.plot("var1", "var2", data=df) This works for most plotting methods, which expect arrays/sequences as -inputs and ``data`` can be anything which supports ``__get_item__`` -(``dict``, ``pandas.DataFrame``,...). +inputs. ``data`` can be anything which supports ``__getitem__`` +(``dict``, ``pandas.DataFrame``, ``h5py``, ...) to access ``array`` like +values with string keys. In addition to this, some other changes were made, which makes working with -``pandas.DataFrames`` easier: - -* For plotting methods which understand a ``label`` keyword argument but the - user does not supply such an argument, this is now implicitly set by either - looking up ``.name`` of the right input or by using the label supplied to - lookup the input in ``data``. In the above examples, this results in an - implicit ``label="var2"`` for both cases. +labeled data (ex ``pandas.Series``) easier: + +* For plotting methods with ``label`` keyword argument, one of the + data inputs is designated as the label source. If the user does not + supply a ``label`` that value object will be introspected for a + label, currently by looking for a ``name`` attribute. If the value + object does not have a ``name`` attribute but was specified by as a + key into the ``data`` kwarg, then the key is used. In the above + examples, this results in an implicit ``label="var2"`` for both + cases. * ``plot()`` now uses the index of a ``Series`` instead of ``np.arange(len(y))``, if no ``x`` argument is supplied.