Skip to content

Add helper function to check that an argument is in a list of strings. #12232

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/api/next_api_changes/2019-01-22-AL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Changed exceptions
``````````````````

- `mpl_toolkits.axes_grid1.axes_size.GetExtentHelper` now raises `ValueError`
for invalid directions instead of `KeyError`.
10 changes: 3 additions & 7 deletions lib/matplotlib/axes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1328,8 +1328,7 @@ def set_adjustable(self, adjustable, share=False):
which the adjustments for aspect ratios are done sequentially
and independently on each Axes as it is drawn.
"""
if adjustable not in ('box', 'datalim'):
raise ValueError("argument must be 'box', or 'datalim'")
cbook._check_in_list(["box", "datalim"], adjustable=adjustable)
if share:
axes = set(self._shared_x_axes.get_siblings(self)
+ self._shared_y_axes.get_siblings(self))
Expand Down Expand Up @@ -2745,9 +2744,7 @@ def grid(self, b=None, which='major', axis='both', **kwargs):
"""
if len(kwargs):
b = True
if axis not in ['x', 'y', 'both']:
raise ValueError("The argument 'axis' must be one of 'x', 'y' or "
"'both'.")
cbook._check_in_list(['x', 'y', 'both'], axis=axis)
if axis in ['x', 'both']:
self.xaxis.grid(b, which=which, **kwargs)
if axis in ['y', 'both']:
Expand Down Expand Up @@ -2963,8 +2960,7 @@ def tick_params(self, axis='both', **kwargs):
also be red. Gridlines will be red and translucent.

"""
if axis not in ['x', 'y', 'both']:
raise ValueError("axis must be one of 'x', 'y' or 'both'")
cbook._check_in_list(['x', 'y', 'both'], axis=axis)
if axis in ['x', 'both']:
xkw = dict(kwargs)
xkw.pop('left', None)
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/axes/_subplots.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import functools
import uuid

from matplotlib import docstring
from matplotlib import cbook, docstring
import matplotlib.artist as martist
from matplotlib.axes._axes import Axes
from matplotlib.gridspec import GridSpec, SubplotSpec
Expand Down
11 changes: 3 additions & 8 deletions lib/matplotlib/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,7 @@ def _set_labelrotation(self, labelrotation):
else:
mode = 'default'
angle = labelrotation
if mode not in ('auto', 'default'):
raise ValueError("Label rotation mode must be 'default' or "
"'auto', not '{}'.".format(mode))
cbook._check_in_list(['auto', 'default'], labelrotation=mode)
self._labelrotation = (mode, angle)

def apply_tickdir(self, tickdir):
Expand Down Expand Up @@ -1269,8 +1267,7 @@ def get_ticklabels(self, minor=False, which=None):
elif which == 'both':
return self.get_majorticklabels() + self.get_minorticklabels()
else:
raise ValueError("`which` must be one of ('minor', 'major', "
"'both') not " + str(which))
cbook._check_in_list(['major', 'minor', 'both'], which=which)
if minor:
return self.get_minorticklabels()
return self.get_majorticklabels()
Expand Down Expand Up @@ -1426,9 +1423,7 @@ def grid(self, b=None, which='major', **kwargs):
'grid will be enabled.')
b = True
which = which.lower()
if which not in ['major', 'minor', 'both']:
raise ValueError("The argument 'which' must be one of 'major', "
"'minor' or 'both'.")
cbook._check_in_list(['major', 'minor', 'both'], which=which)
gridkw = {'grid_' + item[0]: item[1] for item in kwargs.items()}

if which in ['minor', 'both']:
Expand Down
9 changes: 3 additions & 6 deletions lib/matplotlib/backends/backend_ps.py
Original file line number Diff line number Diff line change
Expand Up @@ -838,12 +838,9 @@ def _print_ps(self, outfile, format, *args,
(papertype, ', '.join(papersize)))

orientation = orientation.lower()
if orientation == 'landscape':
isLandscape = True
elif orientation == 'portrait':
isLandscape = False
else:
raise RuntimeError('Orientation must be "portrait" or "landscape"')
cbook._check_in_list(['landscape', 'portrait'],
orientation=orientation)
isLandscape = (orientation == 'landscape')

self.figure.set_dpi(72) # Override the dpi kwarg

Expand Down
6 changes: 2 additions & 4 deletions lib/matplotlib/backends/backend_webagg_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
import numpy as np
import tornado

from matplotlib import backend_bases, cbook, _png
from matplotlib.backends import backend_agg
from matplotlib.backend_bases import _Backend
from matplotlib import backend_bases, _png

_log = logging.getLogger(__name__)

Expand Down Expand Up @@ -163,10 +163,8 @@ def set_image_mode(self, mode):
Note: diff images may not contain transparency, therefore upon
draw this mode may be changed if the resulting image has any
transparent component.

"""
if mode not in ['full', 'diff']:
raise ValueError('image mode must be either full or diff.')
cbook._check_in_list(['full', 'diff'], mode=mode)
if self._current_image_mode != mode:
self._current_image_mode = mode
self.handle_send_image_mode(None)
Expand Down
22 changes: 22 additions & 0 deletions lib/matplotlib/cbook/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2111,6 +2111,12 @@ def _unmultiplied_rgba8888_to_premultiplied_argb32(rgba8888):


def _check_and_log_subprocess(command, logger, **kwargs):
"""
Run *command* using `subprocess.check_output`. If it succeeds, return the
output (stdout and stderr); if not, raise an exception whose text includes
the failed command and captured output. Both the command and the output
are logged at DEBUG level on *logger*.
"""
logger.debug(command)
try:
report = subprocess.check_output(
Expand All @@ -2134,3 +2140,19 @@ def _check_not_matrix(**kwargs):
for k, v in kwargs.items():
if isinstance(v, np.matrix):
raise TypeError(f"Argument {k!r} cannot be a np.matrix")


def _check_in_list(values, **kwargs):
"""
For each *key, value* pair in *kwargs*, check that *value* is in *values*;
if not, raise an appropriate ValueError.

Examples
--------
>>> cbook._check_in_list(["foo", "bar"], arg=arg, other_arg=other_arg)
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though this is just internal, I would like an explanatory example in the docstring because the kwarg usage is a bit special. Maybe even with a note that you'll usually do _check_in_list(values, param=param).

for k, v in kwargs.items():
if v not in values:
raise ValueError(
"{!r} is not a valid value for {}; supported values are {}"
.format(v, k, ', '.join(map(repr, values))))
11 changes: 6 additions & 5 deletions lib/matplotlib/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,8 +464,8 @@ def set_offset_position(self, offset_position):
----------
offset_position : {'screen', 'data'}
"""
if offset_position not in ('screen', 'data'):
raise ValueError("offset_position must be 'screen' or 'data'")
cbook._check_in_list(['screen', 'data'],
offset_position=offset_position)
self._offset_position = offset_position
self.stale = True

Expand Down Expand Up @@ -1490,7 +1490,8 @@ def __init__(self,
coord1 in positions]
self._is_horizontal = False
else:
raise ValueError("orientation must be 'horizontal' or 'vertical'")
cbook._check_in_list(['horizontal', 'vertical'],
orientation=orientation)

LineCollection.__init__(self,
segments,
Expand Down Expand Up @@ -1583,8 +1584,8 @@ def set_orientation(self, orientation=None):
elif orientation.lower() == 'vertical':
is_horizontal = False
else:
raise ValueError("orientation must be 'horizontal' or 'vertical'")

cbook._check_in_list(['horizontal', 'vertical'],
orientation=orientation)
if is_horizontal == self.is_horizontal():
return
self.switch_orientation()
Expand Down
4 changes: 1 addition & 3 deletions lib/matplotlib/contour.py
Original file line number Diff line number Diff line change
Expand Up @@ -842,9 +842,7 @@ def __init__(self, ax, *args,
else:
self.logscale = False

if self.origin not in [None, 'lower', 'upper', 'image']:
raise ValueError("If given, *origin* must be one of [ 'lower' |"
" 'upper' | 'image']")
cbook._check_in_list([None, 'lower', 'upper', 'image'], origin=origin)
if self.extent is not None and len(self.extent) != 4:
raise ValueError("If given, *extent* must be '[ *None* |"
" (x0,x1,y0,y1) ]'")
Expand Down
28 changes: 11 additions & 17 deletions lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1524,23 +1524,17 @@ def subplots(self, nrows=1, ncols=1, sharex=False, sharey=False,
sharex = "all" if sharex else "none"
if isinstance(sharey, bool):
sharey = "all" if sharey else "none"
share_values = ["all", "row", "col", "none"]
if sharex not in share_values:
# This check was added because it is very easy to type
# `subplots(1, 2, 1)` when `subplot(1, 2, 1)` was intended.
# In most cases, no error will ever occur, but mysterious behavior
# will result because what was intended to be the subplot index is
# instead treated as a bool for sharex.
if isinstance(sharex, Integral):
cbook._warn_external("sharex argument to subplots() was an "
"integer. Did you intend to use "
"subplot() (without 's')?")

raise ValueError("sharex [%s] must be one of %s" %
(sharex, share_values))
if sharey not in share_values:
raise ValueError("sharey [%s] must be one of %s" %
(sharey, share_values))
# This check was added because it is very easy to type
# `subplots(1, 2, 1)` when `subplot(1, 2, 1)` was intended.
# In most cases, no error will ever occur, but mysterious behavior
# will result because what was intended to be the subplot index is
# instead treated as a bool for sharex.
if isinstance(sharex, Integral):
cbook._warn_external(
"sharex argument to subplots() was an integer. Did you "
"intend to use subplot() (without 's')?")
cbook._check_in_list(["all", "row", "col", "none"],
sharex=sharex, sharey=sharey)
if subplot_kw is None:
subplot_kw = {}
if gridspec_kw is None:
Expand Down
6 changes: 2 additions & 4 deletions lib/matplotlib/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,8 +759,7 @@ def set_style(self, style):
"""
if style is None:
style = rcParams['font.style']
if style not in ('normal', 'italic', 'oblique'):
raise ValueError("style must be normal, italic or oblique")
cbook._check_in_list(['normal', 'italic', 'oblique'], style=style)
self._slant = style
set_slant = set_style

Expand All @@ -770,8 +769,7 @@ def set_variant(self, variant):
"""
if variant is None:
variant = rcParams['font.variant']
if variant not in ('normal', 'small-caps'):
raise ValueError("variant must be normal or small-caps")
cbook._check_in_list(['normal', 'small-caps'], variant=variant)
self._variant = variant

def set_weight(self, weight):
Expand Down
28 changes: 7 additions & 21 deletions lib/matplotlib/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -1083,8 +1083,7 @@ def set_drawstyle(self, drawstyle):
"""
if drawstyle is None:
drawstyle = 'default'
if drawstyle not in self.drawStyles:
raise ValueError('Unrecognized drawstyle {!r}'.format(drawstyle))
cbook._check_in_list(self.drawStyles, drawstyle=drawstyle)
if self._drawstyle != drawstyle:
self.stale = True
# invalidate to trigger a recache of the path
Expand Down Expand Up @@ -1181,13 +1180,9 @@ def set_linestyle(self, ls):
if ls in [' ', '', 'none']:
ls = 'None'

cbook._check_in_list([*self._lineStyles, *ls_mapper_r], ls=ls)
if ls not in self._lineStyles:
try:
ls = ls_mapper_r[ls]
except KeyError:
raise ValueError("Invalid linestyle {!r}; see docs of "
"Line2D.set_linestyle for valid values"
.format(ls))
ls = ls_mapper_r[ls]
self._linestyle = ls
else:
self._linestyle = '--'
Expand Down Expand Up @@ -1362,9 +1357,7 @@ def set_dash_joinstyle(self, s):
For examples see :doc:`/gallery/lines_bars_and_markers/joinstyle`.
"""
s = s.lower()
if s not in self.validJoin:
raise ValueError('set_dash_joinstyle passed "%s";\n' % (s,)
+ 'valid joinstyles are %s' % (self.validJoin,))
cbook._check_in_list(self.validJoin, s=s)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In cases below you don't use the parameter name but the function name, e.g.

    def set_default_alignment(self, d):
        if d not in ["left", "right", "top", "bottom"]:
        cbook._check_in_list(["left", "right", "top", "bottom"],
            raise ValueError(
                             default_alignment=d)

default_alignment=d vs. s=s. Not terribly important but it would be good to decide on one way or the other and stick to it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to always go with the parameter name. (Neither solution is always great, but heh, you can see the function name in the traceback anyways.)

if self._dashjoinstyle != s:
self.stale = True
self._dashjoinstyle = s
Expand All @@ -1379,10 +1372,7 @@ def set_solid_joinstyle(self, s):
For examples see :doc:`/gallery/lines_bars_and_markers/joinstyle`.
"""
s = s.lower()
if s not in self.validJoin:
raise ValueError('set_solid_joinstyle passed "%s";\n' % (s,)
+ 'valid joinstyles are %s' % (self.validJoin,))

cbook._check_in_list(self.validJoin, s=s)
if self._solidjoinstyle != s:
self.stale = True
self._solidjoinstyle = s
Expand Down Expand Up @@ -1412,9 +1402,7 @@ def set_dash_capstyle(self, s):
s : {'butt', 'round', 'projecting'}
"""
s = s.lower()
if s not in self.validCap:
raise ValueError('set_dash_capstyle passed "%s";\n' % (s,)
+ 'valid capstyles are %s' % (self.validCap,))
cbook._check_in_list(self.validCap, s=s)
if self._dashcapstyle != s:
self.stale = True
self._dashcapstyle = s
Expand All @@ -1428,9 +1416,7 @@ def set_solid_capstyle(self, s):
s : {'butt', 'round', 'projecting'}
"""
s = s.lower()
if s not in self.validCap:
raise ValueError('set_solid_capstyle passed "%s";\n' % (s,)
+ 'valid capstyles are %s' % (self.validCap,))
cbook._check_in_list(self.validCap, s=s)
if self._solidcapstyle != s:
self.stale = True
self._solidcapstyle = s
Expand Down
12 changes: 4 additions & 8 deletions lib/matplotlib/mathtext.py
Original file line number Diff line number Diff line change
Expand Up @@ -3351,14 +3351,10 @@ def parse(self, s, dpi = 72, prop = None):
font_output = StandardPsFonts(prop)
else:
backend = self._backend_mapping[self._output]()
fontset = rcParams['mathtext.fontset']
fontset_class = self._font_type_mapping.get(fontset.lower())
if fontset_class is not None:
font_output = fontset_class(prop, backend)
else:
raise ValueError(
"mathtext.fontset must be either 'cm', 'dejavuserif', "
"'dejavusans', 'stix', 'stixsans', or 'custom'")
fontset = rcParams['mathtext.fontset'].lower()
cbook._check_in_list(self._font_type_mapping, fontset=fontset)
fontset_class = self._font_type_mapping[fontset]
font_output = fontset_class(prop, backend)

fontsize = prop.get_size_in_points()

Expand Down
10 changes: 3 additions & 7 deletions lib/matplotlib/projections/geo.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import numpy as np

from matplotlib import rcParams
from matplotlib import cbook, rcParams
from matplotlib.axes import Axes
import matplotlib.axis as maxis
from matplotlib.patches import Circle
Expand Down Expand Up @@ -118,9 +118,7 @@ def _get_affine_transform(self):
.translate(0.5, 0.5)

def get_xaxis_transform(self, which='grid'):
if which not in ['tick1', 'tick2', 'grid']:
raise ValueError(
"'which' must be one of 'tick1', 'tick2', or 'grid'")
cbook._check_in_list(['tick1', 'tick2', 'grid'], which=which)
return self._xaxis_transform

def get_xaxis_text1_transform(self, pad):
Expand All @@ -130,9 +128,7 @@ def get_xaxis_text2_transform(self, pad):
return self._xaxis_text2_transform, 'top', 'center'

def get_yaxis_transform(self, which='grid'):
if which not in ['tick1', 'tick2', 'grid']:
raise ValueError(
"'which' must be one of 'tick1', 'tick2', or 'grid'")
cbook._check_in_list(['tick1', 'tick2', 'grid'], which=which)
return self._yaxis_transform

def get_yaxis_text1_transform(self, pad):
Expand Down
Loading