diff --git a/doc/api/next_api_changes/2018-02-15-AL-deprecations.rst b/doc/api/next_api_changes/2018-02-15-AL-deprecations.rst index 51198eef7a4e..3e460ba36410 100644 --- a/doc/api/next_api_changes/2018-02-15-AL-deprecations.rst +++ b/doc/api/next_api_changes/2018-02-15-AL-deprecations.rst @@ -36,4 +36,7 @@ The following classes, methods, functions, and attributes are deprecated: - ``text.Annotation.arrow``, The following rcParams are deprecated: + +- ``figure.subplot.left``, ``figure.subplot.right``, etc (instead, set the + single ``figure.subplot`` rcParam to the appropriate dict value). - ``pgf.debug`` (the pgf backend relies on logging), diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 09856d9e1cfc..1a706a378be4 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -906,6 +906,11 @@ def __getitem__(self, key): mplDeprecation) return None + elif key.startswith('figure.subplot.'): + cbook.warn_deprecated( + "3.0", + "{} is deprecated; use figure.subplot instead".format(key)) + val = dict.__getitem__(self, key) if inverse_alt is not None: return inverse_alt(val) diff --git a/lib/matplotlib/cbook/deprecation.py b/lib/matplotlib/cbook/deprecation.py index 9c8c83225ba7..53769e325172 100644 --- a/lib/matplotlib/cbook/deprecation.py +++ b/lib/matplotlib/cbook/deprecation.py @@ -48,7 +48,7 @@ def _generate_deprecation_message( def warn_deprecated( since, message='', name='', alternative='', pending=False, - obj_type='attribute', addendum='', *, removal=''): + obj_type='attribute', addendum='', *, removal='', stacklevel=2): """ Used to display deprecation warning in a standard way. @@ -88,6 +88,9 @@ def warn_deprecated( addendum : str, optional Additional text appended directly to the final message. + stacklevel : int, optional + The stack level used by the `warnings.warn` call. + Examples -------- @@ -100,7 +103,7 @@ def warn_deprecated( """ message = _generate_deprecation_message( since, message, name, alternative, pending, obj_type, removal=removal) - warnings.warn(message, mplDeprecation, stacklevel=2) + warnings.warn(message, mplDeprecation, stacklevel=stacklevel) def deprecated(since, message='', name='', alternative='', pending=False, diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index b97655e8fb39..821945261fe7 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -228,20 +228,18 @@ def reset(): self.hspace = thishspace if self.validate: - if self.left >= self.right: + if not self.left < self.right: reset() - raise ValueError('left cannot be >= right') - - if self.bottom >= self.top: + raise ValueError('One must have right < left') + if not self.bottom < self.top: reset() - raise ValueError('bottom cannot be >= top') + raise ValueError('One must have bottom < top') def _update_this(self, s, val): if val is None: val = getattr(self, s, None) if val is None: - key = 'figure.subplot.' + s - val = rcParams[key] + val = rcParams['figure.subplot'][s] setattr(self, s, val) @@ -1406,8 +1404,11 @@ def clf(self, keep_observers=False): """ Clear the figure. - Set *keep_observers* to True if, for example, - a gui widget is tracking the axes in the figure. + Parameters + ---------- + keep_observers : bool, optional + Set *keep_observers* to True if, for example, + a gui widget is tracking the axes in the figure. """ self.suppressComposite = None self.callbacks = cbook.CallbackRegistry() @@ -1429,6 +1430,7 @@ def clf(self, keep_observers=False): if not keep_observers: self._axobservers = [] self._suptitle = None + self.subplotpars.update(**rcParams['figure.subplot']) if self.get_constrained_layout(): layoutbox.nonetree(self._layoutbox) self.stale = True diff --git a/lib/matplotlib/mpl-data/stylelib/_classic_test.mplstyle b/lib/matplotlib/mpl-data/stylelib/_classic_test.mplstyle index c42222ad8f19..a5969c48c531 100644 --- a/lib/matplotlib/mpl-data/stylelib/_classic_test.mplstyle +++ b/lib/matplotlib/mpl-data/stylelib/_classic_test.mplstyle @@ -307,16 +307,8 @@ figure.autolayout : False # When True, automatically adjust subplot # parameters to make the plot fit the figure figure.frameon : True -# The figure subplot parameters. All dimensions are a fraction of the -# figure width or height -figure.subplot.left : 0.125 # the left side of the subplots of the figure -figure.subplot.right : 0.9 # the right side of the subplots of the figure -figure.subplot.bottom : 0.1 # the bottom of the subplots of the figure -figure.subplot.top : 0.9 # the top of the subplots of the figure -figure.subplot.wspace : 0.2 # the amount of width reserved for space between subplots, - # expressed as a fraction of the average axis width -figure.subplot.hspace : 0.2 # the amount of height reserved for space between subplots, - # expressed as a fraction of the average axis height +# The figure subplot parameters (see matplotlib.figure.SubplotParams). +figure.subplot : {"left": 0.125, "right": 0.9, "bottom": 0.1, "top": 0.9, "wspace": 0.2, "hspace": 0.2} ### IMAGES image.aspect : equal # equal | auto | a number diff --git a/lib/matplotlib/mpl-data/stylelib/classic.mplstyle b/lib/matplotlib/mpl-data/stylelib/classic.mplstyle index 94ae5bf7a4f3..82a0984a4933 100644 --- a/lib/matplotlib/mpl-data/stylelib/classic.mplstyle +++ b/lib/matplotlib/mpl-data/stylelib/classic.mplstyle @@ -311,16 +311,8 @@ figure.autolayout : False # When True, automatically adjust subplot # parameters to make the plot fit the figure figure.frameon : True -# The figure subplot parameters. All dimensions are a fraction of the -# figure width or height -figure.subplot.left : 0.125 # the left side of the subplots of the figure -figure.subplot.right : 0.9 # the right side of the subplots of the figure -figure.subplot.bottom : 0.1 # the bottom of the subplots of the figure -figure.subplot.top : 0.9 # the top of the subplots of the figure -figure.subplot.wspace : 0.2 # the amount of width reserved for space between subplots, - # expressed as a fraction of the average axis width -figure.subplot.hspace : 0.2 # the amount of height reserved for space between subplots, - # expressed as a fraction of the average axis height +# The figure subplot parameters (see matplotlib.figure.SubplotParams). +figure.subplot : {"left": 0.125, "right": 0.9, "bottom": 0.1, "top": 0.9, "wspace": 0.2, "hspace": 0.2} ### IMAGES image.aspect : equal # equal | auto | a number diff --git a/lib/matplotlib/mpl-data/stylelib/fivethirtyeight.mplstyle b/lib/matplotlib/mpl-data/stylelib/fivethirtyeight.mplstyle index 738db39f5f80..44cf365dae9c 100644 --- a/lib/matplotlib/mpl-data/stylelib/fivethirtyeight.mplstyle +++ b/lib/matplotlib/mpl-data/stylelib/fivethirtyeight.mplstyle @@ -34,7 +34,5 @@ font.size:14.0 savefig.edgecolor: f0f0f0 savefig.facecolor: f0f0f0 -figure.subplot.left: 0.08 -figure.subplot.right: 0.95 -figure.subplot.bottom: 0.07 +figure.subplot : {"left": 0.08, "right": 0.95, "bottom": 0.07} figure.facecolor: f0f0f0 diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index 7f2e9806310d..a56f93cd6259 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -15,17 +15,17 @@ """ import six +import ast from collections import Iterable, Mapping from functools import reduce import operator import os -import warnings import re +import warnings from matplotlib import cbook -from matplotlib.cbook import mplDeprecation, deprecated, ls_mapper -from matplotlib.fontconfig_pattern import parse_fontconfig_pattern from matplotlib.colors import is_color_like +from matplotlib.fontconfig_pattern import parse_fontconfig_pattern # Don't let the original cycler collide with our validating cycler from cycler import Cycler, cycler as ccycler @@ -926,7 +926,8 @@ def validate_webagg_address(s): # ls_mapper, and a list of possible strings read from Line2D.set_linestyle _validate_named_linestyle = ValidateInStrings( 'linestyle', - [*ls_mapper.keys(), *ls_mapper.values(), 'None', 'none', ' ', ''], + [*cbook.ls_mapper.keys(), *cbook.ls_mapper.values(), + 'None', 'none', ' ', ''], ignorecase=True) @@ -971,6 +972,45 @@ def _validate_linestyle(ls): "sequence.".format(ls)) +def _validate_subplot(s): + d = ast.literal_eval(s) if isinstance(s, str) else s + try: + # These imports must be delayed because they are not available at + # startup (so we just assume that the default we provide ourselves are + # valid). + from matplotlib import rcParams + from matplotlib.figure import SubplotParams + except ImportError: + pass + else: + d = {**rcParams["figure.subplot"], **d} + SubplotParams(**d) + for key, value in d.items(): + dict.__setitem__(rcParams, "figure.subplot.{}".format(key), value) + return d + + +def _validate_subplot_key(key): + def validator(s): + val = ast.literal_eval(s) if isinstance(s, str) else s + try: + # See above re: delayed imports. + from matplotlib import rcParams + from matplotlib.figure import SubplotParams + except ImportError: + pass + else: + cbook.warn_deprecated( + "3.0", "figure.subplot.{} is deprecated; set the " + "corresponding key in figure.subplot instead.".format(key), + stacklevel=4) + d = {**rcParams["figure.subplot"], key: val} + SubplotParams(**d) + dict.__setitem__(rcParams, "figure.subplot", d) + return val + return validator + + # a map from key -> value, converter defaultParams = { 'backend': ['Agg', validate_backend], # agg is certainly @@ -1317,18 +1357,16 @@ def _validate_linestyle(ls): 'figure.autolayout': [False, validate_bool], 'figure.max_open_warning': [20, validate_int], - 'figure.subplot.left': [0.125, ValidateInterval(0, 1, closedmin=True, - closedmax=True)], - 'figure.subplot.right': [0.9, ValidateInterval(0, 1, closedmin=True, - closedmax=True)], - 'figure.subplot.bottom': [0.11, ValidateInterval(0, 1, closedmin=True, - closedmax=True)], - 'figure.subplot.top': [0.88, ValidateInterval(0, 1, closedmin=True, - closedmax=True)], - 'figure.subplot.wspace': [0.2, ValidateInterval(0, 1, closedmin=True, - closedmax=False)], - 'figure.subplot.hspace': [0.2, ValidateInterval(0, 1, closedmin=True, - closedmax=False)], + 'figure.subplot': [{'left': 0.125, 'right': 0.9, + 'bottom': 0.11, 'top': 0.88, + 'wspace': 0.2, 'hspace': 0.2}, + _validate_subplot], + 'figure.subplot.left': [0.125, _validate_subplot_key('left')], + 'figure.subplot.right': [0.9, _validate_subplot_key('right')], + 'figure.subplot.bottom': [0.11, _validate_subplot_key('bottom')], + 'figure.subplot.top': [0.88, _validate_subplot_key('top')], + 'figure.subplot.wspace': [0.2, _validate_subplot_key('wspace')], + 'figure.subplot.hspace': [0.2, _validate_subplot_key('hspace')], # do constrained_layout. 'figure.constrained_layout.use': [False, validate_bool], diff --git a/lib/matplotlib/testing/decorators.py b/lib/matplotlib/testing/decorators.py index 4bf5d081d9cd..8f1258746923 100644 --- a/lib/matplotlib/testing/decorators.py +++ b/lib/matplotlib/testing/decorators.py @@ -31,7 +31,7 @@ def _do_cleanup(original_units_registry, original_settings): plt.close('all') mpl.rcParams.clear() - mpl.rcParams.update(original_settings) + dict.update(mpl.rcParams, original_settings) matplotlib.units.registry.clear() matplotlib.units.registry.update(original_units_registry) warnings.resetwarnings() # reset any warning filters set in tests diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index d0079012a5e6..d8370e49db0d 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -383,3 +383,11 @@ def test_fspath(fmt, tmpdir): # All the supported formats include the format name (case-insensitive) # in the first 100 bytes. assert fmt.encode("ascii") in file.read(100).lower() + + +def test_clf_subplotpars(): + fig = plt.figure() + orig_subplot_vars = vars(fig.subplotpars).copy() + fig.subplots_adjust(left=0.1) + fig.clf() + assert vars(fig.subplotpars) == orig_subplot_vars diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index 14b589780c0a..dbb9938c9ce7 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -476,7 +476,7 @@ def test_if_rctemplate_is_up_to_date(): continue if k in deprecated: continue - if "verbose" in k: + if "verbose" in k or "figure.subplot." in k: continue found = False for line in rclines: diff --git a/matplotlibrc.template b/matplotlibrc.template index f4549755ed01..edf6c7dcd8aa 100644 --- a/matplotlibrc.template +++ b/matplotlibrc.template @@ -436,15 +436,8 @@ backend : $TEMPLATE_BACKEND #figure.max_open_warning : 20 ## The maximum number of figures to open through ## the pyplot interface before emitting a warning. ## If less than one this feature is disabled. -## The figure subplot parameters. All dimensions are a fraction of the -#figure.subplot.left : 0.125 ## the left side of the subplots of the figure -#figure.subplot.right : 0.9 ## the right side of the subplots of the figure -#figure.subplot.bottom : 0.11 ## the bottom of the subplots of the figure -#figure.subplot.top : 0.88 ## the top of the subplots of the figure -#figure.subplot.wspace : 0.2 ## the amount of width reserved for space between subplots, - ## expressed as a fraction of the average axis width -#figure.subplot.hspace : 0.2 ## the amount of height reserved for space between subplots, - ## expressed as a fraction of the average axis height +## The figure subplot parameters (see matplotlib.figure.SubplotParams). +#figure.subplot : {"left": 0.125, "right": 0.9, "bottom": 0.1, "top": 0.9, "wspace": 0.2, "hspace": 0.2} ## Figure layout #figure.autolayout : False ## When True, automatically adjust subplot