From af170517defe0f4dd7207bee70b82105eb4c0c4a Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 28 Dec 2014 17:20:07 -0500 Subject: [PATCH 1/2] API : tighten validation on pivot in Quiver only accept {'mid', 'middle', 'tip', 'tail'} instead of being super permissive. Closes #3951 --- doc/api/api_changes/2014-12-28_quiver.rst | 12 ++++++++++++ lib/matplotlib/quiver.py | 21 +++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 doc/api/api_changes/2014-12-28_quiver.rst diff --git a/doc/api/api_changes/2014-12-28_quiver.rst b/doc/api/api_changes/2014-12-28_quiver.rst new file mode 100644 index 000000000000..98fc5aed97f5 --- /dev/null +++ b/doc/api/api_changes/2014-12-28_quiver.rst @@ -0,0 +1,12 @@ +Tighted input validation on 'pivot' kwarg to quiver +``````````````````````````````````````````````````` + +Tightened validation so that only {'tip', 'tail', 'mid', and 'middle'} +(but any capitalization) are valid values for the 'pivot' kwarg in +the `Quiver.__init__` (and hence `Axes.quiver` and +`plt.quiver` which both fully delegate to `Quiver`). Previously any +input matching 'mid.*' would be interpreted as 'middle', 'tip.*' as +'tip' and any string not matching one of those patterns as 'tail'. + +The value of `Quiver.pivot` is normalized to be in the set {'tip', +'tail', 'middle'} in `Quiver.__init__`. diff --git a/lib/matplotlib/quiver.py b/lib/matplotlib/quiver.py index 3a7bdaf8f747..76e4a0232881 100644 --- a/lib/matplotlib/quiver.py +++ b/lib/matplotlib/quiver.py @@ -136,7 +136,7 @@ is less than this, plot a dot (hexagon) of this diameter instead. Default is 1. - *pivot*: [ 'tail' | 'middle' | 'tip' ] + *pivot*: [ 'tail' | 'mid' | 'middle' | 'tip' ] The part of the arrow that is at the grid point; the arrow rotates about this point, hence the name *pivot*. @@ -405,6 +405,8 @@ class Quiver(mcollections.PolyCollection): in the draw() method. """ + _PIVOT_VALS = ('tail', 'mid', 'middle', 'tip') + @docstring.Substitution(_quiver_doc) def __init__(self, ax, *args, **kw): """ @@ -430,7 +432,18 @@ def __init__(self, ax, *args, **kw): self.angles = kw.pop('angles', 'uv') self.width = kw.pop('width', None) self.color = kw.pop('color', 'k') - self.pivot = kw.pop('pivot', 'tail') + + pivot = kw.pop('pivot', 'tail').lower() + # validate pivot + if pivot not in self._PIVOT_VALS: + raise ValueError( + 'pivot must be one of {keys}, you passed {inp}'.format( + keys=self._PIVOT_VALS, inp=pivot)) + # normalize to 'middle' + if pivot == 'mid': + pivot = 'middle' + self.pivot = pivot + self.transform = kw.pop('transform', ax.transData) kw.setdefault('facecolors', self.color) kw.setdefault('linewidths', (0,)) @@ -681,9 +694,9 @@ def _h_arrows(self, length): # Now select X0, Y0 if short, otherwise X, Y cbook._putmask(X, short, X0) cbook._putmask(Y, short, Y0) - if self.pivot[:3] == 'mid': + if self.pivot == 'middle': X -= 0.5 * X[:, 3, np.newaxis] - elif self.pivot[:3] == 'tip': + elif self.pivot == 'tip': X = X - X[:, 3, np.newaxis] # numpy bug? using -= does not # work here unless we multiply # by a float first, as with 'mid'. From e6419cfa1615cab4f78d5f66742e805a1919adf1 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 28 Dec 2014 22:16:07 -0500 Subject: [PATCH 2/2] PEP8 : comment-related pep8 Fixes 'violations' we don't test for, all white space in comments. --- lib/matplotlib/quiver.py | 57 +++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/lib/matplotlib/quiver.py b/lib/matplotlib/quiver.py index 76e4a0232881..6d33e55baf54 100644 --- a/lib/matplotlib/quiver.py +++ b/lib/matplotlib/quiver.py @@ -247,8 +247,9 @@ def on_dpi_change(fig): if self_weakref is not None: self_weakref.labelsep = (self_weakref._labelsep_inches*fig.dpi) self_weakref._initialized = False # simple brute force update - # works because _init is called - # at the start of draw. + # works because _init is + # called at the start of + # draw. self._cid = Q.ax.figure.callbacks.connect('dpi_changed', on_dpi_change) @@ -258,7 +259,7 @@ def on_dpi_change(fig): self.fontproperties = kw.pop('fontproperties', dict()) self.kw = kw _fp = self.fontproperties - #boxprops = dict(facecolor='red') + # boxprops = dict(facecolor='red') self.text = mtext.Text( text=label, # bbox=boxprops, horizontalalignment=self.halign[self.labelpos], @@ -465,10 +466,11 @@ def on_dpi_change(fig): self_weakref = weak_self() if self_weakref is not None: self_weakref._new_UV = True # vertices depend on width, span - # which in turn depend on dpi + # which in turn depend on dpi self_weakref._initialized = False # simple brute force update - # works because _init is called - # at the start of draw. + # works because _init is + # called at the start of + # draw. self._cid = self.ax.figure.callbacks.connect('dpi_changed', on_dpi_change) @@ -667,7 +669,7 @@ def _h_arrows(self, length): length = length.reshape(N, 1) # This number is chosen based on when pixel values overflow in Agg # causing rendering errors - #length = np.minimum(length, 2 ** 16) + # length = np.minimum(length, 2 ** 16) np.clip(length, 0, 2 ** 16, out=length) # x, y: normal horizontal arrow x = np.array([0, -self.headaxislength, @@ -870,9 +872,9 @@ class Barbs(mcollections.PolyCollection): From there :meth:`_make_barbs` is used to find the vertices of the polygon to represent the barb based on this information. ''' - #This may be an abuse of polygons here to render what is essentially maybe - #1 triangle and a series of lines. It works fine as far as I can tell - #however. + # This may be an abuse of polygons here to render what is essentially maybe + # 1 triangle and a series of lines. It works fine as far as I can tell + # however. @docstring.interpd def __init__(self, ax, *args, **kw): """ @@ -892,10 +894,11 @@ def __init__(self, ax, *args, **kw): self.flip = kw.pop('flip_barb', False) transform = kw.pop('transform', ax.transData) - #Flagcolor and and barbcolor provide convenience parameters for setting - #the facecolor and edgecolor, respectively, of the barb polygon. We - #also work here to make the flag the same color as the rest of the barb - #by default + # Flagcolor and and barbcolor provide convenience parameters for + # setting the facecolor and edgecolor, respectively, of the barb + # polygon. We also work here to make the flag the same color as the + # rest of the barb by default + if None in (barbcolor, flagcolor): kw['edgecolors'] = 'face' if flagcolor: @@ -903,19 +906,19 @@ def __init__(self, ax, *args, **kw): elif barbcolor: kw['facecolors'] = barbcolor else: - #Set to facecolor passed in or default to black + # Set to facecolor passed in or default to black kw.setdefault('facecolors', 'k') else: kw['edgecolors'] = barbcolor kw['facecolors'] = flagcolor - #Parse out the data arrays from the various configurations supported + # Parse out the data arrays from the various configurations supported x, y, u, v, c = _parse_args(*args) self.x = x self.y = y xy = np.hstack((x[:, np.newaxis], y[:, np.newaxis])) - #Make a collection + # Make a collection barb_size = self._length ** 2 / 4 # Empirically determined mcollections.PolyCollection.__init__(self, [], (barb_size,), offsets=xy, @@ -940,8 +943,8 @@ def _find_tails(self, mag, rounding=True, half=5, full=10, flag=50): a barb is empty (too low to plot any barbs/flags. ''' - #If rounding, round to the nearest multiple of half, the smallest - #increment + # If rounding, round to the nearest multiple of half, the smallest + # increment if rounding: mag = half * (mag / half + 0.5).astype(np.int) @@ -1002,17 +1005,17 @@ def _make_barbs(self, u, v, nflags, nbarbs, half_barb, empty_flag, length, properly align with the vector direction. ''' - #These control the spacing and size of barb elements relative to the - #length of the shaft + # These control the spacing and size of barb elements relative to the + # length of the shaft spacing = length * sizes.get('spacing', 0.125) full_height = length * sizes.get('height', 0.4) full_width = length * sizes.get('width', 0.25) empty_rad = length * sizes.get('emptybarb', 0.15) - #Controls y point where to pivot the barb. + # Controls y point where to pivot the barb. pivot_points = dict(tip=0.0, middle=-length / 2.) - #Check for flip + # Check for flip if flip: full_height = -full_height @@ -1039,11 +1042,11 @@ def _make_barbs(self, u, v, nflags, nbarbs, half_barb, empty_flag, length, barb_list = [] for index, angle in np.ndenumerate(angles): - #If the vector magnitude is too weak to draw anything, plot an - #empty circle instead + # If the vector magnitude is too weak to draw anything, plot an + # empty circle instead if empty_flag[index]: - #We can skip the transform since the circle has no preferred - #orientation + # We can skip the transform since the circle has no preferred + # orientation barb_list.append(empty_barb) continue