diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 94cda34a29d0..391c8cc214c0 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -1978,19 +1978,19 @@ def make_iterable(x): if len(edgecolor) < nbars: edgecolor *= nbars - # FIXME: convert the following to proper input validation - # raising ValueError; don't use assert for this. - assert len(left) == nbars, ("incompatible sizes: argument 'left' must " - "be length %d or scalar" % nbars) - assert len(height) == nbars, ("incompatible sizes: argument 'height' " - "must be length %d or scalar" % - nbars) - assert len(width) == nbars, ("incompatible sizes: argument 'width' " - "must be length %d or scalar" % - nbars) - assert len(bottom) == nbars, ("incompatible sizes: argument 'bottom' " - "must be length %d or scalar" % - nbars) + # input validation + if len(left) != nbars: + raise ValueError("incompatible sizes: argument 'left' must " + "be length %d or scalar" % nbars) + if len(height) != nbars: + raise ValueError("incompatible sizes: argument 'height' " + "must be length %d or scalar" % nbars) + if len(width) != nbars: + raise ValueError("incompatible sizes: argument 'width' " + "must be length %d or scalar" % nbars) + if len(bottom) != nbars: + raise ValueError("incompatible sizes: argument 'bottom' " + "must be length %d or scalar" % nbars) patches = [] @@ -2428,8 +2428,10 @@ def pie(self, x, explode=None, labels=None, colors=None, labels = [''] * len(x) if explode is None: explode = [0] * len(x) - assert(len(x) == len(labels)) - assert(len(x) == len(explode)) + if len(x) != len(labels): + raise ValueError("'label' must be of length 'x'") + if len(x) != len(explode): + raise ValueError("'explode' must be of length 'x'") if colors is None: colors = ('b', 'g', 'r', 'c', 'm', 'y', 'k', 'w') @@ -3686,8 +3688,9 @@ def scatter(self, x, y, s=20, c=None, marker='o', cmap=None, norm=None, collection.update(kwargs) if colors is None: - if norm is not None: - assert(isinstance(norm, mcolors.Normalize)) + if norm is not None and not isinstance(norm, mcolors.Normalize): + msg = "'norm' must be an instance of 'mcolors.Normalize'" + raise ValueError(msg) collection.set_array(np.asarray(c)) collection.set_cmap(cmap) collection.set_norm(norm) @@ -4057,8 +4060,9 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None, bins = np.sort(bins) accum = bins.searchsorted(accum) - if norm is not None: - assert(isinstance(norm, mcolors.Normalize)) + if norm is not None and not isinstance(norm, mcolors.Normalize): + msg = "'norm' must be an instance of 'mcolors.Normalize'" + raise ValueError(msg) collection.set_array(accum) collection.set_cmap(cmap) collection.set_norm(norm) @@ -4673,8 +4677,9 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, if not self._hold: self.cla() - if norm is not None: - assert(isinstance(norm, mcolors.Normalize)) + if norm is not None and not isinstance(norm, mcolors.Normalize): + msg = "'norm' must be an instance of 'mcolors.Normalize'" + raise ValueError(msg) if aspect is None: aspect = rcParams['image.aspect'] self.set_aspect(aspect) @@ -5000,8 +5005,9 @@ def pcolor(self, *args, **kwargs): collection.set_alpha(alpha) collection.set_array(C) - if norm is not None: - assert(isinstance(norm, mcolors.Normalize)) + if norm is not None and not isinstance(norm, mcolors.Normalize): + msg = "'norm' must be an instance of 'mcolors.Normalize'" + raise ValueError(msg) collection.set_cmap(cmap) collection.set_norm(norm) collection.set_clim(vmin, vmax) @@ -5149,8 +5155,9 @@ def pcolormesh(self, *args, **kwargs): antialiased=antialiased, shading=shading, **kwargs) collection.set_alpha(alpha) collection.set_array(C) - if norm is not None: - assert(isinstance(norm, mcolors.Normalize)) + if norm is not None and not isinstance(norm, mcolors.Normalize): + msg = "'norm' must be an instance of 'mcolors.Normalize'" + raise ValueError(msg) collection.set_cmap(cmap) collection.set_norm(norm) collection.set_clim(vmin, vmax) @@ -5274,8 +5281,9 @@ def pcolorfast(self, *args, **kwargs): cmap = kwargs.pop('cmap', None) vmin = kwargs.pop('vmin', None) vmax = kwargs.pop('vmax', None) - if norm is not None: - assert(isinstance(norm, mcolors.Normalize)) + if norm is not None and not isinstance(norm, mcolors.Normalize): + msg = "'norm' must be an instance of 'mcolors.Normalize'" + raise ValueError(msg) C = args[-1] nr, nc = C.shape diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index c8e5b3cbb955..9a97ebb83ca9 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2104,7 +2104,10 @@ def draw_artist(self, a): caches the renderer. It is used to efficiently update Axes data (axis ticks, labels, etc are not updated) """ - assert self._cachedRenderer is not None + if self._cachedRenderer is None: + msg = ('draw_artist can only be used after an initial draw which' + ' caches the render') + raise AttributeError(msg) a.draw(self._cachedRenderer) def redraw_in_frame(self): @@ -2113,7 +2116,10 @@ def redraw_in_frame(self): caches the renderer. It is used to efficiently update Axes data (axis ticks, labels, etc are not updated) """ - assert self._cachedRenderer is not None + if self._cachedRenderer is None: + msg = ('redraw_in_frame can only be used after an initial draw' + ' which caches the render') + raise AttributeError(msg) self.draw(self._cachedRenderer, inframe=True) def get_renderer_cache(self): diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index dba6fd15f686..4aed76dc85b7 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1728,11 +1728,13 @@ def set_label_position(self, position): ACCEPTS: [ 'top' | 'bottom' ] """ - assert position == 'top' or position == 'bottom' if position == 'top': self.label.set_verticalalignment('baseline') - else: + elif position == 'bottom': self.label.set_verticalalignment('top') + else: + msg = "Position accepts only [ 'top' | 'bottom' ]" + raise ValueError(msg) self.label_position = position def _update_label_position(self, bboxes, bboxes2): @@ -2032,13 +2034,15 @@ def set_label_position(self, position): ACCEPTS: [ 'left' | 'right' ] """ - assert position == 'left' or position == 'right' self.label.set_rotation_mode('anchor') self.label.set_horizontalalignment('center') if position == 'left': self.label.set_verticalalignment('bottom') - else: + elif position == 'right': self.label.set_verticalalignment('top') + else: + msg = "Position accepts only [ 'left' | 'right' ]" + raise ValueError(msg) self.label_position = position def _update_label_position(self, bboxes, bboxes2): @@ -2083,13 +2087,14 @@ def _update_offset_text_position(self, bboxes, bboxes2): ) def set_offset_position(self, position): - assert position == 'left' or position == 'right' - x, y = self.offsetText.get_position() if position == 'left': x = 0 - else: + elif position == 'right': x = 1 + else: + msg = "Position accepts only [ 'left' | 'right' ]" + raise ValueError(msg) self.offsetText.set_ha(position) self.offsetText.set_position((x, y)) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index edcc81f95f89..bd2072f1f6f6 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -969,7 +969,11 @@ def set_clip_path(self, path): Set the clip path and transformation. Path should be a :class:`~matplotlib.transforms.TransformedPath` instance. """ - assert path is None or isinstance(path, transforms.TransformedPath) + if path is not None and not isinstance(path, + transforms.TransformedPath): + msg = ("Path should be a matplotlib.transforms.TransformedPath" + "instance.") + raise ValueError(msg) self._clippath = path def set_dashes(self, dash_offset, dash_list): diff --git a/lib/matplotlib/blocking_input.py b/lib/matplotlib/blocking_input.py index 2ed22b918664..d7035221e8eb 100644 --- a/lib/matplotlib/blocking_input.py +++ b/lib/matplotlib/blocking_input.py @@ -30,6 +30,8 @@ from matplotlib.cbook import is_sequence_of_strings import matplotlib.lines as mlines +import warnings + class BlockingInput(object): """ @@ -38,8 +40,8 @@ class BlockingInput(object): """ def __init__(self, fig, eventslist=()): self.fig = fig - assert is_sequence_of_strings( - eventslist), "Requires a sequence of event name strings" + if not is_sequence_of_strings(eventslist): + raise ValueError("Requires a sequence of event name strings") self.eventslist = eventslist def on_event(self, event): @@ -95,7 +97,8 @@ def __call__(self, n=1, timeout=30): Blocking call to retrieve n events """ - assert isinstance(n, int), "Requires an integer argument" + if not isinstance(n, int): + raise ValueError("Requires an integer argument") self.n = n self.events = [] @@ -146,9 +149,9 @@ def post_event(self): """ This will be called to process events """ - assert len(self.events) > 0, "No events yet" - - if self.events[-1].name == 'key_press_event': + if len(self.events) == 0: + warnings.warn("No events yet") + elif self.events[-1].name == 'key_press_event': self.key_event() else: self.mouse_event() @@ -359,9 +362,10 @@ def post_event(self): """ Determines if it is a key event """ - assert len(self.events) > 0, "No events yet" - - self.keyormouse = self.events[-1].name == 'key_press_event' + if len(self.events) == 0: + warnings.warn("No events yet") + else: + self.keyormouse = self.events[-1].name == 'key_press_event' def __call__(self, timeout=30): """ diff --git a/lib/matplotlib/contour.py b/lib/matplotlib/contour.py index 4bceb7417d60..9d6a73f12c8f 100644 --- a/lib/matplotlib/contour.py +++ b/lib/matplotlib/contour.py @@ -857,10 +857,12 @@ def __init__(self, ax, *args, **kwargs): else: self.logscale = False - if self.origin is not None: - assert(self.origin in ['lower', 'upper', 'image']) - if self.extent is not None: - assert(len(self.extent) == 4) + if self.origin not in [None, 'lower', 'upper', 'image']: + raise ValueError("If given, *origin* must be one of [ 'lower' |" + " 'upper' | 'image']") + if self.extent is not None and len(self.extent) != 4: + raise ValueError("If given, *extent* must be '[ *None* |" + " (x0,x1,y0,y1) ]'") if self.colors is not None and cmap is not None: raise ValueError('Either colors or cmap must be None') if self.origin == 'image': diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 986870c78140..f14c8c8c64a6 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -872,7 +872,9 @@ def add_axes(self, *args, **kwargs): if isinstance(args[0], Axes): a = args[0] - assert(a.get_figure() is self) + if a.get_figure() is not self: + msg = "The Axes must have been created in the present figure" + raise ValueError(msg) else: rect = args[0] projection_class, kwargs, key = process_projection_requirements( @@ -946,7 +948,10 @@ def add_subplot(self, *args, **kwargs): if isinstance(args[0], SubplotBase): a = args[0] - assert(a.get_figure() is self) + if a.get_figure() is not self: + msg = ("The Subplot must have been created in the present" + " figure") + raise ValueError(msg) # make a key for the subplot (which includes the axes object id # in the hash) key = self._make_key(*args, **kwargs) @@ -1104,7 +1109,10 @@ def draw_artist(self, a): draw :class:`matplotlib.artist.Artist` instance *a* only -- this is available only after the figure is drawn """ - assert self._cachedRenderer is not None + if self._cachedRenderer is None: + msg = ('draw_artist can only be used after an initial draw which' + ' caches the render') + raise AttributeError(msg) a.draw(self._cachedRenderer) def get_axes(self): diff --git a/lib/matplotlib/finance.py b/lib/matplotlib/finance.py index 02b020b58e08..630a531dc885 100644 --- a/lib/matplotlib/finance.py +++ b/lib/matplotlib/finance.py @@ -736,7 +736,7 @@ def candlestick_ohlc(ax, quotes, width=0.2, colorup='k', colordown='r', def _candlestick(ax, quotes, width=0.2, colorup='k', colordown='r', - alpha=1.0, ochl=True): + alpha=1.0, ochl=True): """ Plot the time, open, high, low, close as a vertical line ranging @@ -816,6 +816,63 @@ def _candlestick(ax, quotes, width=0.2, colorup='k', colordown='r', return lines, patches +def _check_input(opens, closes, highs, lows, miss=-1): + """Checks that *opens*, *highs*, *lows* and *closes* have the same length. + NOTE: this code assumes if any value open, high, low, close is + missing (*-1*) they all are missing + + Parameters + ---------- + ax : `Axes` + an Axes instance to plot to + opens : sequence + sequence of opening values + highs : sequence + sequence of high values + lows : sequence + sequence of low values + closes : sequence + sequence of closing values + miss : int + identifier of the missing data + + Raises + ------ + ValueError + if the input sequences don't have the same length + """ + + def _missing(sequence, miss=-1): + """Returns the index in *sequence* of the missing data, identified by + *miss* + + Parameters + ---------- + sequence : + sequence to evaluate + miss : + identifier of the missing data + + Returns + ------- + where_miss: numpy.ndarray + indices of the missing data + """ + return np.where(np.array(sequence) == miss)[0] + + same_length = len(opens) == len(highs) == len(lows) == len(closes) + _missopens = _missing(opens) + same_missing = ((_missopens == _missing(highs)).all() and + (_missopens == _missing(lows)).all() and + (_missopens == _missing(closes)).all()) + + if not (same_length and same_missing): + msg = ("*opens*, *highs*, *lows* and *closes* must have the same" + " length. NOTE: this code assumes if any value open, high," + " low, close is missing (*-1*) they all must be missing.") + raise ValueError(msg) + + def plot_day_summary2_ochl(ax, opens, closes, highs, lows, ticksize=4, colorup='k', colordown='r', ): @@ -860,6 +917,9 @@ def plot_day_summary2_ohlc(ax, opens, highs, lows, closes, ticksize=4, """Represent the time, open, high, low, close as a vertical line ranging from low to high. The left tick is the open and the right tick is the close. + *opens*, *highs*, *lows* and *closes* must have the same length. + NOTE: this code assumes if any value open, high, low, close is + missing (*-1*) they all are missing Parameters ---------- @@ -885,8 +945,8 @@ def plot_day_summary2_ohlc(ax, opens, highs, lows, closes, ticksize=4, ret : list a list of lines added to the axes """ - # note this code assumes if any value open, high, low, close is - # missing they all are missing + + _check_input(opens, highs, lows, closes) rangeSegments = [((i, low), (i, high)) for i, low, high in zip(xrange(len(lows)), lows, highs) if low != -1] @@ -919,10 +979,6 @@ def plot_day_summary2_ohlc(ax, opens, highs, lows, closes, ticksize=4, colors = [colord[open < close] for open, close in zip(opens, closes) if open != -1 and close != -1] - assert(len(rangeSegments) == len(offsetsOpen)) - assert(len(offsetsOpen) == len(offsetsClose)) - assert(len(offsetsClose) == len(colors)) - useAA = 0, # use tuple here lw = 1, # and here rangeCollection = LineCollection(rangeSegments, @@ -1012,6 +1068,9 @@ def candlestick2_ohlc(ax, opens, highs, lows, closes, width=4, """Represent the open, close as a bar line and high low range as a vertical line. + NOTE: this code assumes if any value open, low, high, close is + missing they all are missing + Parameters ---------- @@ -1040,8 +1099,7 @@ def candlestick2_ohlc(ax, opens, highs, lows, closes, width=4, (lineCollection, barCollection) """ - # note this code assumes if any value open, low, high, close is - # missing they all are missing + _check_input(opens, highs, lows, closes) delta = width / 2. barVerts = [((i - delta, open), @@ -1066,8 +1124,6 @@ def candlestick2_ohlc(ax, opens, highs, lows, closes, width=4, for open, close in zip(opens, closes) if open != -1 and close != -1] - assert(len(barVerts) == len(rangeSegments)) - useAA = 0, # use tuple here lw = 0.5, # and here rangeCollection = LineCollection(rangeSegments, diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 2428147218bb..a0f12fb946a3 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -520,7 +520,8 @@ def set_filterrad(self, filterrad): ACCEPTS: positive float """ r = float(filterrad) - assert(r > 0) + if r <= 0: + raise ValueError("The filter radius must be a positive number") self._filterrad = r def get_filterrad(self): diff --git a/lib/matplotlib/mlab.py b/lib/matplotlib/mlab.py index 2baedbb952aa..75127fe55e83 100644 --- a/lib/matplotlib/mlab.py +++ b/lib/matplotlib/mlab.py @@ -1485,7 +1485,8 @@ def cohere_pairs(X, ij, NFFT=256, Fs=2, detrend=detrend_none, # of every channel. If preferSpeedOverMemory, cache the conjugate # as well if cbook.iterable(window): - assert(len(window) == NFFT) + if len(window) != NFFT: + raise ValueError("The length of the window must be equal to NFFT") windowVals = window else: windowVals = window(np.ones(NFFT, X.dtype)) @@ -3577,7 +3578,8 @@ def stineman_interp(xi, x, y, yp=None): # Cast key variables as float. x = np.asarray(x, np.float_) y = np.asarray(y, np.float_) - assert x.shape == y.shape + if x.shape != y.shape: + raise ValueError("'x' and 'y' must be of same shape") if yp is None: yp = slopes(x, y) @@ -3824,7 +3826,8 @@ def poly_below(xmin, xs, ys): ys = numpy.asarray(ys) Nx = len(xs) Ny = len(ys) - assert(Nx == Ny) + if Nx != Ny: + raise ValueError("'xs' and 'ys' must have the same length") x = xmin*numpy.ones(2*Nx) y = numpy.ones(2*Nx) x[:Nx] = xs diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index fa82e91607cc..eea54c41fa64 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -3094,10 +3094,10 @@ def ensure_quadratic_bezier(path): its control points if true. """ segments = list(path.iter_segments()) - assert len(segments) == 2 - - assert segments[0][1] == Path.MOVETO - assert segments[1][1] == Path.CURVE3 + if ((len(segments) != 2) or (segments[0][1] != Path.MOVETO) or + (segments[1][1] != Path.CURVE3)): + msg = "'path' it's not a valid quadratic bezier curve" + raise ValueError(msg) return list(segments[0][0]) + list(segments[1][0]) @@ -3805,7 +3805,7 @@ def transmute(self, path, mutation_size, linewidth): class Wedge(_Base): """ - Wedge(?) shape. Only wokrs with a quadratic bezier curve. The + Wedge(?) shape. Only works with a quadratic bezier curve. The begin point has a width of the tail_width and the end point has a width of 0. At the middle, the width is shrink_factor*tail_width. diff --git a/lib/matplotlib/path.py b/lib/matplotlib/path.py index 90acebb999ae..6f9af4296af5 100644 --- a/lib/matplotlib/path.py +++ b/lib/matplotlib/path.py @@ -136,21 +136,26 @@ def __init__(self, vertices, codes=None, _interpolation_steps=1, else: vertices = np.asarray(vertices, np.float_) + if (vertices.ndim != 2) or (vertices.shape[1] != 2): + msg = "'vertices' must be a 2D list or array with shape Nx2" + raise ValueError(msg) + if codes is not None: codes = np.asarray(codes, self.code_type) - assert codes.ndim == 1 - assert len(codes) == len(vertices) - if len(codes): - assert codes[0] == self.MOVETO + if (codes.ndim != 1) or len(codes) != len(vertices): + msg = ("'codes' must be a 1D list or array with the same" + " length of 'vertices'") + raise ValueError(msg) + if len(codes) and codes[0] != self.MOVETO: + msg = ("The first element of 'code' must be equal to 'MOVETO':" + " {0}") + raise ValueError(msg.format(self.MOVETO)) elif closed: codes = np.empty(len(vertices), dtype=self.code_type) codes[0] = self.MOVETO codes[1:-1] = self.LINETO codes[-1] = self.CLOSEPOLY - assert vertices.ndim == 2 - assert vertices.shape[1] == 2 - self._vertices = vertices self._codes = codes self._interpolation_steps = _interpolation_steps @@ -320,7 +325,8 @@ def make_compound_path_from_polys(cls, XY): # the CLOSEPOLY; the vert for the closepoly is ignored but we still # need it to keep the codes aligned with the vertices numpolys, numsides, two = XY.shape - assert(two == 2) + if two != 2: + raise ValueError("The third dimension of 'XY' must be 2") stride = numsides + 1 nverts = numpolys * stride verts = np.zeros((nverts, 2)) diff --git a/lib/matplotlib/projections/geo.py b/lib/matplotlib/projections/geo.py index a81dad29782c..827ed86c2d21 100644 --- a/lib/matplotlib/projections/geo.py +++ b/lib/matplotlib/projections/geo.py @@ -129,7 +129,9 @@ def _get_affine_transform(self): .translate(0.5, 0.5) def get_xaxis_transform(self,which='grid'): - assert which in ['tick1','tick2','grid'] + if which not in ['tick1','tick2','grid']: + msg = "'which' must be on of [ 'tick1' | 'tick2' | 'grid' ]" + raise ValueError(msg) return self._xaxis_transform def get_xaxis_text1_transform(self, pad): @@ -139,7 +141,9 @@ def get_xaxis_text2_transform(self, pad): return self._xaxis_text2_transform, 'top', 'center' def get_yaxis_transform(self,which='grid'): - assert which in ['tick1','tick2','grid'] + if which not in ['tick1','tick2','grid']: + msg = "'which' must be one of [ 'tick1' | 'tick2' | 'grid' ]" + raise ValueError(msg) return self._yaxis_transform def get_yaxis_text1_transform(self, pad): diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index 5e815a0654a6..c78faf7f728a 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -340,7 +340,9 @@ def _set_lim_and_transforms(self): ) def get_xaxis_transform(self,which='grid'): - assert which in ['tick1','tick2','grid'] + if which not in ['tick1','tick2','grid']: + msg = "'which' must be one of [ 'tick1' | 'tick2' | 'grid' ]" + raise ValueError(msg) return self._xaxis_transform def get_xaxis_text1_transform(self, pad): @@ -350,7 +352,9 @@ def get_xaxis_text2_transform(self, pad): return self._xaxis_text2_transform, 'center', 'center' def get_yaxis_transform(self,which='grid'): - assert which in ['tick1','tick2','grid'] + if which not in ['tick1','tick2','grid']: + msg = "'which' must be on of [ 'tick1' | 'tick2' | 'grid' ]" + raise ValueError(msg) return self._yaxis_transform def get_yaxis_text1_transform(self, pad): diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index 51289832c6b1..38e8f705e486 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -272,19 +272,21 @@ def validate_colorlist(s): 'return a list of colorspecs' if isinstance(s, six.string_types): return [validate_color(c.strip()) for c in s.split(',')] - else: - assert type(s) in [list, tuple] + elif type(s) in (list, tuple): return [validate_color(c) for c in s] - + else: + msg = "'s' must be of type [ string | list | tuple ]" + raise ValueError(msg) def validate_stringlist(s): 'return a list' if isinstance(s, six.string_types): return [six.text_type(v.strip()) for v in s.split(',') if v.strip()] - else: - assert type(s) in [list, tuple] + elif type(s) in (list, tuple): return [six.text_type(v) for v in s if v] - + else: + msg = "'s' must be of type [ string | list | tuple ]" + raise ValueError(msg) validate_orientation = ValidateInStrings( 'orientation', ['landscape', 'portrait']) diff --git a/lib/matplotlib/sankey.py b/lib/matplotlib/sankey.py index b5f8e735e066..c9af96fd9af2 100755 --- a/lib/matplotlib/sankey.py +++ b/lib/matplotlib/sankey.py @@ -29,7 +29,8 @@ # argument specifies the direction of the arrows. The "main" # inputs/outputs are now specified via an orientation of 0, and there may # be several of each. -# --Added assertions to catch common calling errors +# --Changed assertions to ValueError to catch common calling errors (by +# Francesco Montesano, franz.bergesung@gmail.com) # --Added the physical unit as a string argument to be used in the labels, so # that the values of the flows can usually be applied automatically # --Added an argument for a minimum magnitude below which flows are not shown @@ -150,17 +151,21 @@ def __init__(self, ax=None, scale=1.0, unit='', format='%G', gap=0.25, .. plot:: mpl_examples/api/sankey_demo_basics.py """ # Check the arguments. - assert gap >= 0, ( + if gap < 0: + raise ValueError( "The gap is negative.\nThis isn't allowed because it " "would cause the paths to overlap.") - assert radius <= gap, ( + if radius > gap: + raise ValueError( "The inner radius is greater than the path spacing.\n" "This isn't allowed because it would cause the paths to overlap.") - assert head_angle >= 0, ( + if head_angle < 0: + raise ValueError( "The angle is negative.\nThis isn't allowed " "because it would cause inputs to look like " "outputs and vice versa.") - assert tolerance >= 0, ( + if tolerance < 0: + raise ValueError( "The tolerance is negative.\nIt must be a magnitude.") # Create axes if necessary. @@ -470,20 +475,23 @@ def add(self, patchlabel='', flows=None, orientations=None, labels='', rotation /= 90.0 if orientations is None: orientations = [0, 0] - assert len(orientations) == n, ( + if len(orientations) != n: + raise ValueError( "orientations and flows must have the same length.\n" "orientations has length %d, but flows has length %d." % (len(orientations), n)) if labels != '' and getattr(labels, '__iter__', False): # iterable() isn't used because it would give True if labels is a # string - assert len(labels) == n, ( + if len(labels) != n: + raise ValueError( "If labels is a list, then labels and flows must have the " "same length.\nlabels has length %d, but flows has length %d." % (len(labels), n)) else: labels = [labels] * n - assert trunklength >= 0, ( + if trunklength < 0: + raise ValueError( "trunklength is negative.\nThis isn't allowed, because it would " "cause poor layout.") if np.absolute(np.sum(flows)) > self.tolerance: @@ -504,28 +512,35 @@ def add(self, patchlabel='', flows=None, orientations=None, labels='', "cause poor layout.\nConsider changing the scale so" " that the scaled sum is approximately 1.0." % gain, 'helpful') if prior is not None: - assert prior >= 0, "The index of the prior diagram is negative." - assert min(connect) >= 0, ( + if prior < 0: + raise ValueError("The index of the prior diagram is negative.") + if min(connect) < 0: + raise ValueError( "At least one of the connection indices is negative.") - assert prior < len(self.diagrams), ( + if prior >= len(self.diagrams): + raise ValueError( "The index of the prior diagram is %d, but there are " "only %d other diagrams.\nThe index is zero-based." % (prior, len(self.diagrams))) - assert connect[0] < len(self.diagrams[prior].flows), ( + if connect[0] >= len(self.diagrams[prior].flows): + raise ValueError( "The connection index to the source diagram is %d, but " "that diagram has only %d flows.\nThe index is zero-based." % (connect[0], len(self.diagrams[prior].flows))) - assert connect[1] < n, ( + if connect[1] >= n: + raise ValueError( "The connection index to this diagram is %d, but this diagram" "has only %d flows.\n The index is zero-based." % (connect[1], n)) - assert self.diagrams[prior].angles[connect[0]] is not None, ( + if self.diagrams[prior].angles[connect[0]] is None: + raise ValueError( "The connection cannot be made. Check that the magnitude " "of flow %d of diagram %d is greater than or equal to the " "specified tolerance." % (connect[0], prior)) flow_error = (self.diagrams[prior].flows[connect[0]] + flows[connect[1]]) - assert abs(flow_error) < self.tolerance, ( + if abs(flow_error) >= self.tolerance: + raise ValueError( "The scaled sum of the connected flows is %f, which is not " "within the tolerance (%f)." % (flow_error, self.tolerance)) @@ -556,9 +571,10 @@ def add(self, patchlabel='', flows=None, orientations=None, labels='', if is_input is not None: angles[i] = RIGHT else: - assert orient == -1, ( + if orient != -1: + raise ValueError( "The value of orientations[%d] is %d, " - "but it must be -1, 0, or 1." % (i, orient)) + "but it must be [ -1 | 0 | 1 ]." % (i, orient)) if is_input: angles[i] = UP elif not is_input: @@ -566,7 +582,8 @@ def add(self, patchlabel='', flows=None, orientations=None, labels='', # Justify the lengths of the paths. if iterable(pathlengths): - assert len(pathlengths) == n, ( + if len(pathlengths) != n: + raise ValueError( "If pathlengths is a list, then pathlengths and flows must " "have the same length.\npathlengths has length %d, but flows " "has length %d." % (len(pathlengths), n)) diff --git a/lib/matplotlib/scale.py b/lib/matplotlib/scale.py index 69158ad8b23f..7193d1e7edf6 100644 --- a/lib/matplotlib/scale.py +++ b/lib/matplotlib/scale.py @@ -440,9 +440,12 @@ def __init__(self, axis, **kwargs): subs = kwargs.pop('subsy', None) linscale = kwargs.pop('linscaley', 1.0) - assert base > 1.0 - assert linthresh > 0.0 - assert linscale > 0.0 + if base <= 1.0: + raise ValueError("'basex/basey' must be larger than 1") + if linthresh <= 0.0: + raise ValueError("'linthreshx/linthreshy' must be positive") + if linscale <= 0.0: + raise ValueError("'linscalex/linthreshy' must be positive") self._transform = self.SymmetricalLogTransform(base, linthresh, diff --git a/lib/matplotlib/spines.py b/lib/matplotlib/spines.py index 2673c46b92f2..f73ee15fbc73 100644 --- a/lib/matplotlib/spines.py +++ b/lib/matplotlib/spines.py @@ -71,7 +71,9 @@ def __init__(self, axes, spine_type, path, **kwargs): # non-rectangular axes is currently implemented, and this lets # them pass through the spines machinery without errors.) self._position = None - assert isinstance(path, matplotlib.path.Path) + if not isinstance(path, matplotlib.path.Path): + msg = "'path' must be an instance of 'matplotlib.path.Path'" + raise ValueError(msg) self._path = path # To support drawing both linear and circular spines, this @@ -176,7 +178,8 @@ def is_frame_like(self): position = ('axes', 0.5) elif position == 'zero': position = ('data', 0) - assert len(position) == 2, "position should be 2-tuple" + if len(position) != 2: + raise ValueError("position should be 2-tuple") position_type, amount = position if position_type == 'outward' and amount == 0: return True @@ -372,8 +375,12 @@ def set_position(self, position): # special positions pass else: - assert len(position) == 2, "position should be 'center' or 2-tuple" - assert position[0] in ['outward', 'axes', 'data'] + if len(position) != 2: + raise ValueError("position should be 'center' or 2-tuple") + if position[0] not in ['outward', 'axes', 'data']: + msg = ("position[0] should be in [ 'outward' | 'axes' |" + " 'data' ]") + raise ValueError(msg) self._position = position self._calc_offset_transform() diff --git a/lib/matplotlib/streamplot.py b/lib/matplotlib/streamplot.py index 1bfacdb70339..3708afa647db 100644 --- a/lib/matplotlib/streamplot.py +++ b/lib/matplotlib/streamplot.py @@ -90,7 +90,9 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, use_multicolor_lines = isinstance(color, np.ndarray) if use_multicolor_lines: - assert color.shape == grid.shape + if color.shape != grid.shape: + msg = "If 'color' is given, must have the shape of 'Grid(x,y)'" + raise ValueError(msg) line_colors = [] color = np.ma.masked_invalid(color) else: @@ -98,7 +100,9 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, arrow_kw['color'] = color if isinstance(linewidth, np.ndarray): - assert linewidth.shape == grid.shape + if linewidth.shape != grid.shape: + msg = "If 'linewidth' is given, must have the shape of 'Grid(x,y)'" + raise ValueError(msg) line_kw['linewidth'] = [] else: line_kw['linewidth'] = linewidth @@ -108,8 +112,9 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, arrow_kw['zorder'] = zorder ## Sanity checks. - assert u.shape == grid.shape - assert v.shape == grid.shape + if (u.shape != grid.shape) or (v.shape != grid.shape): + msg = "'u' and 'v' must be of shape 'Grid(x,y)'" + raise ValueError(msg) u = np.ma.masked_invalid(u) v = np.ma.masked_invalid(v) @@ -256,19 +261,25 @@ class Grid(object): """Grid of data.""" def __init__(self, x, y): - if len(x.shape) == 2: - x_row = x[0] - assert np.allclose(x_row, x) + if x.ndim == 1: + pass + elif x.ndim == 2: + x_row = x[0, :] + if not np.allclose(x_row, x): + raise ValueError("The rows of 'x' must be equal") x = x_row else: - assert len(x.shape) == 1 + raise ValueError("'x' can have at maximum 2 dimensions") - if len(y.shape) == 2: + if y.ndim == 1: + pass + elif y.ndim == 2: y_col = y[:, 0] - assert np.allclose(y_col, y.T) + if not np.allclose(y_col, y.T): + raise ValueError("The columns of 'y' must be equal") y = y_col else: - assert len(y.shape) == 1 + raise ValueError("'y' can have at maximum 2 dimensions") self.nx = len(x) self.ny = len(y) @@ -304,10 +315,12 @@ class StreamMask(object): def __init__(self, density): if np.isscalar(density): - assert density > 0 + if density <= 0: + raise ValueError("If a scalar, 'density' must be positive") self.nx = self.ny = int(30 * density) else: - assert len(density) == 2 + if len(density) != 2: + raise ValueError("'density' can have at maximum 2 dimensions") self.nx = int(30 * density[0]) self.ny = int(30 * density[1]) self._mask = np.zeros((self.ny, self.nx)) diff --git a/lib/matplotlib/table.py b/lib/matplotlib/table.py index 497c1eb49ad4..c918457d3479 100644 --- a/lib/matplotlib/table.py +++ b/lib/matplotlib/table.py @@ -154,7 +154,7 @@ class Table(Artist): Each entry in the table can be either text or patches. - Column widths and row heights for the table can be specifified. + Column widths and row heights for the table can be specified. Return value is a sequence of text, line and patch instances that make up the table @@ -482,12 +482,17 @@ def table(ax, rows = len(cellText) cols = len(cellText[0]) for row in cellText: - assert len(row) == cols + if len(row) != cols: + msg = "Each row in 'cellText' must have {0} columns" + raise ValueError(msg.format(cols)) if cellColours is not None: - assert len(cellColours) == rows + if len(cellColours) != rows: + raise ValueError("'cellColours' must have {0} rows".format(rows)) for row in cellColours: - assert len(row) == cols + if len(row) != cols: + msg = "Each row in 'cellColours' must have {0} columns" + raise ValueError(msg.format(cols)) else: cellColours = ['w' * cols] * rows @@ -506,7 +511,8 @@ def table(ax, rowColours = 'w' * rows if rowLabels is not None: - assert len(rowLabels) == rows + if len(rowLabels) != rows: + raise ValueError("'rowLabels' must be of length {0}".format(rows)) # If we have column labels, need to shift # the text and colour arrays down 1 row @@ -519,9 +525,6 @@ def table(ax, elif colColours is None: colColours = 'w' * cols - if rowLabels is not None: - assert len(rowLabels) == rows - # Set up cell colours if not given if cellColours is None: cellColours = ['w' * cols] * rows diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index ec92da9ee246..8265cb77dad2 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -227,8 +227,8 @@ def test_validators(): (['a', 'b'], ['a', 'b']), (('a', 'b'), ['a', 'b']), ((1, 2), ['1', '2'])), - 'fail': ((dict(), AssertionError), - (1, AssertionError),) + 'fail': ((dict(), ValueError), + (1, ValueError),) }, {'validator': validate_nseq_int(2), 'success': ((_, [1, 2]) diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index 14fdd4533bc3..034277fcd094 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -1474,7 +1474,8 @@ def __init__(self, artist, ref_coord, unit="points"): self.set_unit(unit) def set_unit(self, unit): - assert unit in ["points", "pixels"] + if unit not in ["points", "pixels"]: + raise ValueError("'unit' must be one of [ 'points' | 'pixels' ]") self._unit = unit def get_unit(self): diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index b374d5ce5693..69075fd9595a 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -464,7 +464,8 @@ def set_powerlimits(self, lims): greater than 1e4. See also :meth:`set_scientific`. ''' - assert len(lims) == 2, "argument must be a sequence of length 2" + if len(lims) != 2: + raise ValueError("'lims' must be a sequence of length 2") self._powerlimits = lims def format_data_short(self, value): @@ -1201,7 +1202,8 @@ def closeto(x, y): class Base(object): 'this solution has some hacks to deal with floating point inaccuracies' def __init__(self, base): - assert(base > 0) + if base <= 0: + raise ValueError("'base' must be positive") self._base = base def lt(self, x): diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index e6c1c98ccfaf..fcac999115bf 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -593,7 +593,8 @@ def shrunk_to_aspect(self, box_aspect, container=None, fig_aspect=1.0): *fig_aspect*, so that *box_aspect* can also be given as a ratio of the absolute dimensions, not the relative dimensions. """ - assert box_aspect > 0 and fig_aspect > 0 + if box_aspect <= 0 or fig_aspect <= 0: + raise ValueError("'box_aspect' and 'fig_aspect' must be positive") if container is None: container = self w, h = container.size @@ -719,7 +720,8 @@ def union(bboxes): """ Return a :class:`Bbox` that contains all of the given bboxes. """ - assert(len(bboxes)) + if not len(bboxes): + raise ValueError("'bboxes' cannot be empty") if len(bboxes) == 1: return bboxes[0] @@ -1062,10 +1064,15 @@ def __init__(self, bbox, transform, **kwargs): *transform*: a 2D :class:`Transform` """ - assert bbox.is_bbox - assert isinstance(transform, Transform) - assert transform.input_dims == 2 - assert transform.output_dims == 2 + if not bbox.is_bbox: + raise ValueError("'bbox' is not a bbox") + if not isinstance(transform, Transform): + msg = ("'transform' must be an instance of" + " 'matplotlib.transform.Transform'") + raise ValueError(msg) + if transform.input_dims != 2 or transform.output_dims != 2: + msg = "The input and output dimensions of 'transform' must be 2" + raise ValueError(msg) BboxBase.__init__(self, **kwargs) self._bbox = bbox @@ -1384,7 +1391,9 @@ def transform_point(self, point): The transformed point is returned as a sequence of length :attr:`output_dims`. """ - assert len(point) == self.input_dims + if len(point) != self.input_dims: + msg = "The length of 'point' must be 'self.input_dims'" + raise ValueError(msg) return self.transform(np.asarray([point]))[0] def transform_path(self, path): @@ -1454,12 +1463,12 @@ def transform_angles(self, angles, pts, radians=False, pushoff=1e-5): if self.input_dims != 2 or self.output_dims != 2: raise NotImplementedError('Only defined in 2D') - # pts must be array with 2 columns for x,y - assert pts.shape[1] == 2 + if pts.shape[1] != 2: + raise ValueError("'pts' must be array with 2 columns for x,y") - # angles must be a column vector and have same number of - # rows as pts - assert np.prod(angles.shape) == angles.shape[0] == pts.shape[0] + if angles.ndim!=1 or angles.shape[0] != pts.shape[0]: + msg = "'angles' must be a column vector and have same number of" + msg += " rows as 'pts'" # Convert to radians if desired if not radians: @@ -1516,7 +1525,10 @@ def __init__(self, child): *child*: A class:`Transform` instance. This child may later be replaced with :meth:`set`. """ - assert isinstance(child, Transform) + if not isinstance(child, Transform): + msg = ("'child' must be an instance of" + " 'matplotlib.transform.Transform'") + raise ValueError(msg) Transform.__init__(self) self.input_dims = child.input_dims self.output_dims = child.output_dims @@ -1571,8 +1583,11 @@ def set(self, child): The new child must have the same number of input and output dimensions as the current child. """ - assert child.input_dims == self.input_dims - assert child.output_dims == self.output_dims + if (child.input_dims != self.input_dims or + child.output_dims != self.output_dims): + msg = ("The new child must have the same number of input and" + " output dimensions as the current child.") + raise ValueError(msg) self._set(child) @@ -1822,7 +1837,10 @@ def set(self, other): Set this transformation from the frozen copy of another :class:`Affine2DBase` object. """ - assert isinstance(other, Affine2DBase) + if not isinstance(other, Affine2DBase): + msg = ("'other' must be an instance of" + " 'matplotlib.transform.Affine2DBase'") + raise ValueError(msg) self._mtx = other.get_matrix() self.invalidate() @@ -2152,10 +2170,13 @@ def __init__(self, x_transform, y_transform, **kwargs): can determine automatically which kind of blended transform to create. """ - assert x_transform.is_affine - assert y_transform.is_affine - assert x_transform.is_separable - assert y_transform.is_separable + is_affine = x_transform.is_affine and y_transform.is_affine + is_separable = x_transform.is_separable and y_transform.is_separable + is_correct = is_affine and is_separable + if not is_correct: + msg = ("Both *x_transform* and *y_transform* must be 2D affine" + " transforms.") + raise ValueError(msg) Transform.__init__(self, **kwargs) self._x = x_transform @@ -2232,7 +2253,10 @@ def __init__(self, a, b, **kwargs): which can automatically choose the best kind of composite transform instance to create. """ - assert a.output_dims == b.input_dims + if a.output_dims != b.input_dims: + msg = ("The output dimension of 'a' must be equal to the input" + " dimensions of 'b'") + raise ValueError(msg) self.input_dims = a.input_dims self.output_dims = b.output_dims @@ -2357,11 +2381,14 @@ def __init__(self, a, b, **kwargs): which can automatically choose the best kind of composite transform instance to create. """ - assert a.output_dims == b.input_dims + if not a.is_affine or not b.is_affine: + raise ValueError("'a' and 'b' must be affine transforms") + if a.output_dims != b.input_dims: + msg = ("The output dimension of 'a' must be equal to the input" + " dimensions of 'b'") + raise ValueError(msg) self.input_dims = a.input_dims self.output_dims = b.output_dims - assert a.is_affine - assert b.is_affine Affine2DBase.__init__(self, **kwargs) self._a = a @@ -2436,8 +2463,9 @@ def __init__(self, boxin, boxout, **kwargs): Create a new :class:`BboxTransform` that linearly transforms points from *boxin* to *boxout*. """ - assert boxin.is_bbox - assert boxout.is_bbox + if not boxin.is_bbox or not boxout.is_bbox: + msg = "'boxin' and 'boxout' must be bbox" + raise ValueError(msg) Affine2DBase.__init__(self, **kwargs) self._boxin = boxin @@ -2480,7 +2508,8 @@ def __init__(self, boxout, **kwargs): Create a new :class:`BboxTransformTo` that linearly transforms points from the unit bounding box to *boxout*. """ - assert boxout.is_bbox + if not boxout.is_bbox: + raise ValueError("'boxout' must be bbox") Affine2DBase.__init__(self, **kwargs) self._boxout = boxout @@ -2538,7 +2567,8 @@ class BboxTransformFrom(Affine2DBase): is_separable = True def __init__(self, boxin, **kwargs): - assert boxin.is_bbox + if not boxin.is_bbox: + raise ValueError("'boxin' must be bbox") Affine2DBase.__init__(self, **kwargs) self._boxin = boxin @@ -2613,7 +2643,10 @@ def __init__(self, path, transform): Create a new :class:`TransformedPath` from the given :class:`~matplotlib.path.Path` and :class:`Transform`. """ - assert isinstance(transform, Transform) + if not isinstance(transform, Transform): + msg = ("'transform' must be an instance of" + " 'matplotlib.transform.Transform'") + raise ValueError(msg) TransformNode.__init__(self) self._path = path diff --git a/lib/matplotlib/tri/tripcolor.py b/lib/matplotlib/tri/tripcolor.py index 449ec03a0ed5..7db662ed2257 100644 --- a/lib/matplotlib/tri/tripcolor.py +++ b/lib/matplotlib/tri/tripcolor.py @@ -138,7 +138,9 @@ def tripcolor(ax, *args, **kwargs): collection.set_alpha(alpha) collection.set_array(C) if norm is not None: - assert(isinstance(norm, Normalize)) + if not isinstance(norm, Normalize): + msg = "'norm' must be an instance of 'Normalize'" + raise ValueError(msg) collection.set_cmap(cmap) collection.set_norm(norm) if vmin is not None or vmax is not None: diff --git a/lib/matplotlib/widgets.py b/lib/matplotlib/widgets.py index 6675903b6877..6eba3574f7e4 100644 --- a/lib/matplotlib/widgets.py +++ b/lib/matplotlib/widgets.py @@ -1306,7 +1306,9 @@ def __init__(self, ax, onselect, direction, minspan=None, useblit=False, if rectprops is None: rectprops = dict(facecolor='red', alpha=0.5) - assert direction in ['horizontal', 'vertical'], 'Must choose horizontal or vertical for direction' + if direction not in ['horizontal', 'vertical']: + msg = "direction must be in [ 'horizontal' | 'vertical' ]" + raise ValueError(msg) self.direction = direction self.rect = None @@ -1558,7 +1560,9 @@ def __init__(self, ax, onselect, drawtype='box', self.minspanx = minspanx self.minspany = minspany - assert(spancoords in ('data', 'pixels')) + if spancoords not in ('data', 'pixels'): + msg = "'spancoords' must be one of [ 'data' | 'pixels' ]" + raise ValueError(msg) self.spancoords = spancoords self.drawtype = drawtype