diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index d0c3584563bf..4ab982ac4303 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -110,6 +110,21 @@ used, which, in the case of straight contours was sometimes quite distant from the requested location. Much more precise label positioning is now possible. +Quickly find rcParams +--------------------- +Phil Elson made it easier to search for rcParameters by passing a +valid regular expression to :func:`matplotlib.RcParams.find_all`. +:class:`matplotlib.RcParams` now also has a pretty repr and str representation +so that search results are printed prettily: + + >>> import matplotlib + >>> print(matplotlib.rcParams.find_all('\.size')) + RcParams({'font.size': 12, + 'xtick.major.size': 4, + 'xtick.minor.size': 2, + 'ytick.major.size': 4, + 'ytick.minor.size': 2}) + .. _whats-new-1-2-2: new in matplotlib 1.2.2 diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 997e86f40c93..cd300bd428f9 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -706,8 +706,8 @@ class RcParams(dict): :mod:`matplotlib.rcsetup` """ - validate = dict([ (key, converter) for key, (default, converter) in \ - defaultParams.iteritems() ]) + validate = dict((key, converter) for key, (default, converter) in \ + defaultParams.iteritems()) msg_depr = "%s is deprecated and replaced with %s; please use the latter." msg_depr_ignore = "%s is deprecated and ignored. Use %s" @@ -738,6 +738,19 @@ def __getitem__(self, key): key = alt return dict.__getitem__(self, key) + def __repr__(self): + import pprint + class_name = self.__class__.__name__ + indent = len(class_name) + 1 + repr_split = pprint.pformat(dict(self), indent=1, + width=80 - indent).split('\n') + repr_indented = ('\n' + ' ' * indent).join(repr_split) + return '{0}({1})'.format(class_name, repr_indented) + + def __str__(self): + return '\n'.join('{0}: {1}'.format(k, v) + for k, v in sorted(self.items())) + def keys(self): """ Return sorted list of keys. @@ -750,7 +763,25 @@ def values(self): """ Return values in order of sorted keys. """ - return [self[k] for k in self.iterkeys()] + return [self[k] for k in self.keys()] + + def find_all(self, pattern): + """ + Return the subset of this RcParams dictionary whose keys match, + using :func:`re.search`, the given ``pattern``. + + .. note:: + + Changes to the returned dictionary are *not* propagated to + the parent RcParams dictionary. + + """ + import re + pattern_re = re.compile(pattern) + return RcParams((key, value) + for key, value in self.items() + if pattern_re.search(key)) + def rc_params(fail_on_error=False): 'Return the default params updated from the values in the rc file' diff --git a/lib/matplotlib/tests/__init__.py b/lib/matplotlib/tests/__init__.py index bd133a4f6645..995ba452dbfa 100644 --- a/lib/matplotlib/tests/__init__.py +++ b/lib/matplotlib/tests/__init__.py @@ -1,8 +1,12 @@ from __future__ import print_function from matplotlib import rcParams, rcdefaults, use +import difflib + + _multiprocess_can_split_ = True + def setup(): use('Agg', warn=False) # use Agg backend for these tests @@ -13,3 +17,19 @@ def setup(): rcParams['font.family'] = 'Bitstream Vera Sans' rcParams['text.hinting'] = False rcParams['text.hinting_factor'] = 8 + + +def assert_str_equal(reference_str, test_str, + format_str='String {str1} and {str2} do not match:\n{differences}'): + """ + Assert the two strings are equal. If not, fail and print their diffs using difflib. + + """ + if reference_str != test_str: + diff = difflib.unified_diff(reference_str.splitlines(1), + test_str.splitlines(1), + 'Reference', 'Test result', + '', '', 0) + raise ValueError(format_str.format(str1=reference_str, + str2=test_str, + differences=''.join(diff))) diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index 4666c41274c5..efee5b0f94e5 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -1,6 +1,9 @@ import os import matplotlib as mpl +from matplotlib.tests import assert_str_equal + + mpl.rc('text', usetex=False) mpl.rc('lines', linewidth=22) @@ -34,5 +37,39 @@ def test_rcparams(): mpl.rcParams['lines.linewidth'] = linewidth +def test_RcParams_class(): + rc = mpl.RcParams({'font.cursive': ['Apple Chancery', + 'Textile', + 'Zapf Chancery', + 'cursive'], + 'font.family': 'sans-serif', + 'font.weight': 'normal', + 'font.size': 12}) + + + expected_repr = """ +RcParams({'font.cursive': ['Apple Chancery', + 'Textile', + 'Zapf Chancery', + 'cursive'], + 'font.family': 'sans-serif', + 'font.size': 12, + 'font.weight': 'normal'})""".lstrip() + + assert_str_equal(expected_repr, repr(rc)) + + expected_str = """ +font.cursive: ['Apple Chancery', 'Textile', 'Zapf Chancery', 'cursive'] +font.family: sans-serif +font.size: 12 +font.weight: normal""".lstrip() + + assert_str_equal(expected_str, str(rc)) + + # test the find_all functionality + assert ['font.cursive', 'font.size'] == sorted(rc.find_all('i[vz]').keys()) + assert ['font.family'] == rc.find_all('family').keys() + if __name__ == '__main__': - test_rcparams() + import nose + nose.runmodule(argv=['-s', '--with-doctest'], exit=False)