8000 Merge pull request #3564 from tacaswell/rcparam_validation_fix · matplotlib/matplotlib@27bdd59 · GitHub
[go: up one dir, main page]

Skip to content

Commit 27bdd59

Browse files
committed
Merge pull request #3564 from tacaswell/rcparam_validation_fix
Rcparam validation fix
2 parents 11c0985 + 726495e commit 27bdd59

File tree

4 files changed

+232
-64
lines changed

4 files changed

+232
-64
lines changed

lib/matplotlib/__init__.py

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
import six
106106
import sys
107107
import distutils.version
108+
from itertools import chain
108109

109110
__version__ = str('1.4.x')
110111
__version__numpy__ = str('1.6') # minimum required numpy version
@@ -244,6 +245,7 @@ def _is_writable_dir(p):
244245

245246
return True
246247

248+
247249
class Verbose:
248250
"""
249251
A class to handle reporting. Set the fileo attribute to any file
@@ -806,6 +808,18 @@ def matplotlib_fname():
806808
_deprecated_ignore_map = {
807809
}
808810

811+
_obsolete_set = set(['tk.pythoninspect', ])
812+
_all_deprecated = set(chain(_deprecated_ignore_map,
813+
_deprecated_map, _obsolete_set))
814+
815+
_rcparam_warn_str = ("Trying to set {key} to {value} via the {func} "
816+
"method of RcParams which does not validate cleanly. "
817+
"This warning will turn into an Exception in 1.5. "
818+
"If you think {value} should validate correctly for "
819+
"rcParams[{key}] "
820+
"please create an issue on github."
821+
)
822+
809823

810824
class RcParams(dict):
811825

@@ -817,14 +831,27 @@ class RcParams(dict):
817831
"""
818832

819833
validate = dict((key, converter) for key, (default, converter) in
820-
six.iteritems(defaultParams))
834+
six.iteritems(defaultParams)
835+
if key not in _all_deprecated)
821836
msg_depr = "%s is deprecated and replaced with %s; please use the latter."
822837
msg_depr_ignore = "%s is deprecated and ignored. Use %s"
823838

839+
# validate values on the way in
840+
def __init__(self, *args, **kwargs):
841+
for k, v in six.iteritems(dict(*args, **kwargs)):
842+
try:
843+
self[k] = v
844+
except (ValueError, RuntimeError):
845+
# force the issue
846+
warnings.warn(_rcparam_warn_str.format(key=repr(k),
847+
value=repr(v),
848+
func='__init__'))
849+
dict.__setitem__(self, k, v)
850+
824851
def __setitem__(self, key, val):
825852
try:
826853
if key in _deprecated_map:
827-
alt_key, alt_val = _deprecated_map[key]
854+
alt_key, alt_val = _deprecated_map[key]
828855
warnings.warn(self.msg_depr % (key, alt_key))
829856
key = alt_key
830857
val = alt_val(val)
@@ -843,7 +870,7 @@ def __setitem__(self, key, val):
843870

844871
def __getitem__(self, key):
845872
if key in _deprecated_map:
846-
alt_key, alt_val = _deprecated_map[key]
873+
alt_key, alt_val = _deprecated_map[key]
847874
warnings.warn(self.msg_depr % (key, alt_key))
848875
key = alt_key
849876
elif key in _deprecated_ignore_map:
@@ -852,6 +879,22 @@ def __getitem__(self, key):
852879
key = alt
853880
return dict.__getitem__(self, key)
854881

882+
# http://stackoverflow.com/questions/2390827/how-to-properly-subclass-dict-and-override-get-set
883+
# the default dict `update` does not use __setitem__
884+
# so rcParams.update(...) (such as in seaborn) side-steps
885+
# all of the validation over-ride update to force
886+
# through __setitem__
887+
def update(self, *args, **kwargs):
888+
for k, v in six.iteritems(dict(*args, **kwargs)):
889+
try:
890+
self[k] = v
891+
except (ValueError, RuntimeError):
892+
# force the issue
893+
warnings.warn(_rcparam_warn_str.format(key=repr(k),
894+
value=repr(v),
895+
func='update'))
896+
dict.__setitem__(self, k, v)
897+
855898
def __repr__(self):
856899
import pprint
857900
class_name = self.__class__.__name__
@@ -905,8 +948,9 @@ def rc_params(fail_on_error=False):
905948
if not os.path.exists(fname):
906949
# this should never happen, default in mpl-data should always be found
907950
message = 'could not find rc file; returning defaults'
908-
ret = RcParams([(key, default) for key, (default, _) in \
909-
six.iteritems(defaultParams)])
951+
ret = RcParams([(key, default) for key, (default, _) in
952+
six.iteritems(defaultParams)
953+
if key not in _all_deprecated])
910954
warnings.warn(message)
911955
return ret
912956

@@ -1028,7 +1072,8 @@ def rc_params_from_file(fname, fail_on_error=False, use_default_template=True):
10281072
return config_from_file
10291073

10301074
iter_params = six.iteritems(defaultParams)
1031-
config = RcParams([(key, default) for key, (default, _) in iter_params])
1075+
config = RcParams([(key, default) for key, (default, _) in iter_params
1076+
if key not in _all_deprecated])
10321077
config.update(config_from_file)
10331078

10341079
verbose.set_level(config['verbose.level'])
@@ -1070,16 +1115,20 @@ def rc_params_from_file(fname, fail_on_error=False, use_default_template=True):
10701115

10711116
rcParamsOrig = rcParams.copy()
10721117

1073-
rcParamsDefault = RcParams([(key, default) for key, (default, converter) in \
1074-
six.iteritems(defaultParams)])
1118+
rcParamsDefault = RcParams([(key, default) for key, (default, converter) in
1119+
six.iteritems(defaultParams)
1120+
if key not in _all_deprecated])
10751121

1076-
rcParams['ps.usedistiller'] = checkdep_ps_distiller(rcParams['ps.usedistiller'])
1122+
1123+
rcParams['ps.usedistiller'] = checkdep_ps_distiller(
1124+
rcParams['ps.usedistiller'])
10771125
rcParams['text.usetex'] = checkdep_usetex(rcParams['text.usetex'])
10781126

10791127
if rcParams['axes.formatter.use_locale']:
10801128
import locale
10811129
locale.setlocale(locale.LC_ALL, '')
10821130

1131+
10831132
def rc(group, **kwargs):
10841133
"""
10851134
Set the current rc params. Group is the grouping for the rc, e.g.,

lib/matplotlib/rcsetup.py

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ def validate_any(s):
6666

6767
def validate_path_exists(s):
6868
"""If s is a path, return s, else False"""
69+
if s is None:
70+
return None
6971
if os.path.exists(s):
7072
return s
7173
else:
@@ -172,50 +174,54 @@ def validate_maskedarray(v):
172174
' please delete it from your matplotlibrc file')
173175

174176

175-
class validate_nseq_float:
177+
_seq_err_msg = ('You must supply exactly {n:d} values, you provided '
178+
'{num:d} values: {s}')
179+
180+
_str_err_msg = ('You must supply exactly {n:d} comma-separated values, '
181+
'you provided '
182+
'{num:d} comma-separated values: {s}')
183+
184+
185+
class validate_nseq_float(object):
176186
def __init__(self, n):
177187
self.n = n
178188

179189
def __call__(self, s):
180190
"""return a seq of n floats or raise"""
181191
if isinstance(s, six.string_types):
182-
ss = s.split(',')
183-
if len(ss) != self.n:
184-
raise ValueError(
185-
'You must supply exactly %d comma separated values' %
186-
self.n)
187-
try:
188-
return [float(val) for val in ss]
189-
except ValueError:
190-
raise ValueError('Could not convert all entries to floats')
192+
s = s.split(',')
193+
err_msg = _str_err_msg
191194
else:
192-
assert type(s) in (list, tuple)
193-
if len(s) != self.n:
194-
raise ValueError('You must supply exactly %d values' % self.n)
195+
err_msg = _seq_err_msg
196+
197+
if len(s) != self.n:
198+
raise ValueError(err_msg.format(n=self.n, num=len(s), s=s))
199+
200+
try:
195201
return [float(val) for val in s]
202+
except ValueError:
203+
raise ValueError('Could not convert all entries to floats')
196204

197205

198-
class validate_nseq_int:
206+
class validate_nseq_int(object):
199207
def __init__(self, n):
200208
self.n = n
201209

202210
def __call__(self, s):
203211
"""return a seq of n ints or raise"""
204212
if isinstance(s, six.string_types):
205-
ss = s.split(',')
206-
if len(ss) != self.n:
207-
raise ValueError(
208-
'You must supply exactly %d comma separated values' %
209-
self.n)
210-
try:
211-
return [int(val) for val in ss]
212-
except ValueError:
213-
raise ValueError('Could not convert all entries to ints')
213+
s = s.split(',')
214+
err_msg = _str_err_msg
214215
else:
215-
assert type(s) in (list, tuple)
216-
if len(s) != self.n:
217-
raise ValueError('You must supply exactly %d values' % self.n)
216+
err_msg = _seq_err_msg
217+
218+
if len(s) != self.n:
219+
raise ValueError(err_msg.format(n=self.n, num=len(s), s=s))
220+
221+
try:
218222
return [int(val) for val in s]
223+
except ValueError:
224+
raise ValueError('Could not convert all entries to ints')
219225

220226

221227
def validate_color(s):
@@ -263,10 +269,10 @@ def validate_colorlist(s):
263269
def validate_stringlist(s):
264270
'return a list'
265271
if isinstance(s, six.string_types):
266-
return [v.strip() for v in s.split(',')]
272+
return [six.text_type(v.strip()) for v in s.split(',') if v.strip()]
267273
else:
268274
assert type(s) in [list, tuple]
269-
return [six.text_type(v) for v in s]
275+
return [six.text_type(v) for v in s if v]
270276

271277

272278
validate_orientation = ValidateInStrings(
@@ -517,7 +523,7 @@ def __call__(self, s):
517523

518524

519525
## font props
520-
'font.family': ['sans-serif', validate_stringlist], # used by text object
526+
'font.family': [['sans-serif'], validate_stringlist], # used by text object
521527
'font.style': ['normal', six.text_type],
522528
'font.variant': ['normal', six.text_type],
523529
'font.stretch': ['normal', six.text_type],
@@ -776,14 +782,14 @@ def __call__(self, s):
776782
'keymap.home': [['h', 'r', 'home'], validate_stringlist],
777783
'keymap.back': [['left', 'c', 'backspace'], validate_stringlist],
778784
'keymap.forward': [['right', 'v'], validate_stringlist],
779-
'keymap.pan': ['p', validate_stringlist],
780-
'keymap.zoom': ['o', validate_stringlist],
781-
'keymap.save': [('s', 'ctrl+s'), validate_stringlist],
782-
'keymap.quit': [('ctrl+w', 'cmd+w'), validate_stringlist],
783-
'keymap.grid': ['g', validate_stringlist],
784-
'keymap.yscale': ['l', validate_stringlist],
785+
'keymap.pan': [['p'], validate_stringlist],
786+
'keymap.zoom': [['o'], validate_stringlist],
787+
'keymap.save': [['s', 'ctrl+s'], validate_stringlist],
788+
'keymap.quit': [['ctrl+w', 'cmd+w'], validate_stringlist],
789+
'keymap.grid': [['g'], validate_stringlist],
790+
'keymap.yscale': [['l'], validate_stringlist],
785791
'keymap.xscale': [['k', 'L'], validate_stringlist],
786-
'keymap.all_axes': ['a', validate_stringlist],
792+
'keymap.all_axes': [['a'], validate_stringlist],
787793

788794
# sample data
789795
'examples.directory': ['', six.text_type],
@@ -797,21 +803,21 @@ def __call__(self, s):
797803
# Path to FFMPEG binary. If just binary name, subprocess uses $PATH.
798804
'animation.ffmpeg_path': ['ffmpeg', six.text_type],
799805

800-
## Additional arguments for ffmpeg movie writer (using pipes)
801-
'animation.ffmpeg_args': ['', validate_stringlist],
806+
# Additional arguments for ffmpeg movie writer (using pipes)
807+
'animation.ffmpeg_args': [[], validate_stringlist],
802808
# Path to AVConv binary. If just binary name, subprocess uses $PATH.
803809
'animation.avconv_path': ['avconv', six.text_type],
804810
# Additional arguments for avconv movie writer (using pipes)
805-
'animation.avconv_args': ['', validate_stringlist],
811+
'animation.avconv_args': [[], validate_stringlist],
806812
# Path to MENCODER binary. If just binary name, subprocess uses $PATH.
807813
'animation.mencoder_path': ['mencoder', six.text_type],
808814
# Additional arguments for mencoder movie writer (using pipes)
809-
'animation.mencoder_args': ['', validate_stringlist],
815+
'animation.mencoder_args': [[], validate_stringlist],
810816
# Path to convert binary. If just binary name, subprocess uses $PATH
811817
'animation.convert_path': ['convert', six.text_type],
812818
# Additional arguments for mencoder movie writer (using pipes)
813819

814-
'animation.convert_args': ['', validate_stringlist]}
820+
'animation.convert_args': [[], validate_stringlist]}
815821

816822

817823
if __name__ == '__main__':

lib/matplotlib/tests/test_animation.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def test_save_animation_smoketest():
2828
yield check_save_animation, writer, extension
2929

3030

31-
@with_setup(CleanupTest.setup_class, CleanupTest.teardown_class)
31+
@cleanup
3232
def check_save_animation(writer, extension='mp4'):
3333
if not animation.writers.is_available(writer):
3434
raise KnownFailureTest("writer '%s' not available on this system"
@@ -39,6 +39,9 @@ def check_save_animation(writer, extension='mp4'):
3939
fig, ax = plt.subplots()
4040
line, = ax.plot([], [])
4141

42+
ax.set_xlim(0, 10)
43+
ax.set_ylim(-1, 1)
44+
4245
def init():
4346
line.set_data([], [])
4447
return line,

0 commit comments

Comments
 (0)
0