Skip to content

Commit b8673bd

Browse files
committed
MNT: make signature of GridSpec.update explicit
Previously, it used **kwargs, because it needs to distinguish between "value not given" and given value is None. A while ago, we have introduced the UNSET sentinel for these cases (in the context of Artist .set()). The PR moves this sentinel to `matplotlib._api` to make it usable also for gridspec. Technically, there's an API change here, because the old code raised an AttibuteError for invalid keys. This is now a TypeError by the standards when an invalid keyword argument is used. I consider the old behavior suboptimal as it was not adhereing to standards. I will let the change go without notice because I assume nobody will explicitly catch the exception of an invalid parameter name passed to `GridSpec.update()`.
1 parent 492a478 commit b8673bd

File tree

6 files changed

+40
-17
lines changed

6 files changed

+40
-17
lines changed

lib/matplotlib/_api/__init__.py

+9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@
2525
MatplotlibDeprecationWarning)
2626

2727

28+
# A sentinel value for optional arguments, when None cannot be used as
29+
# default because we need to distinguish between None passed explicitly
30+
# and parameter not given. Usage: def foo(arg=_api.UNSET):
31+
class _Unset:
32+
def __repr__(self):
33+
return "<UNSET>"
34+
UNSET = _Unset()
35+
36+
2837
class classproperty:
2938
"""
3039
Like `property`, but also triggers on access via the class, and it is the

lib/matplotlib/_api/__init__.pyi

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ from .deprecation import ( # noqa: F401, re-exported API
1818

1919
_T = TypeVar("_T")
2020

21+
class _Unset: ...
22+
2123
class classproperty(Any):
2224
def __init__(
2325
self,

lib/matplotlib/artist.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,6 @@ def _stale_axes_callback(self, val):
107107
_XYPair = namedtuple("_XYPair", "x y")
108108

109109

110-
class _Unset:
111-
def __repr__(self):
112-
return "<UNSET>"
113-
_UNSET = _Unset()
114-
115-
116110
class Artist:
117111
"""
118112
Abstract base class for objects that render into a FigureCanvas.
@@ -166,7 +160,7 @@ def _update_set_signature_and_docstring(cls):
166160
"""
167161
cls.set.__signature__ = Signature(
168162
[Parameter("self", Parameter.POSITIONAL_OR_KEYWORD),
169-
*[Parameter(prop, Parameter.KEYWORD_ONLY, default=_UNSET)
163+
*[Parameter(prop, Parameter.KEYWORD_ONLY, default=_api.UNSET)
170164
for prop in ArtistInspector(cls).get_setters()
171165
if prop not in Artist._PROPERTIES_EXCLUDED_FROM_SET]])
172166
cls.set._autogenerated_signature = True

lib/matplotlib/artist.pyi

-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ class _XYPair(NamedTuple):
2626
x: ArrayLike
2727
y: ArrayLike
2828

29-
class _Unset: ...
30-
3129
class Artist:
3230
zorder: float
3331
stale_callback: Callable[[Artist, bool], None] | None

lib/matplotlib/gridspec.py

+17-7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import matplotlib as mpl
2020
from matplotlib import _api, _pylab_helpers, _tight_layout
21+
from matplotlib._api import UNSET as _UNSET
2122
from matplotlib.transforms import Bbox
2223

2324
_log = logging.getLogger(__name__)
@@ -366,7 +367,8 @@ def __init__(self, nrows, ncols, figure=None,
366367

367368
_AllowedKeys = ["left", "bottom", "right", "top", "wspace", "hspace"]
368369

369-
def update(self, **kwargs):
370+
def update(self, *, left=_UNSET, bottom=_UNSET, right=_UNSET, top=_UNSET,
371+
wspace=_UNSET, hspace=_UNSET):
370372
"""
371373
Update the subplot parameters of the grid.
372374
@@ -377,15 +379,23 @@ def update(self, **kwargs):
377379
----------
378380
left, right, top, bottom : float or None, optional
379381
Extent of the subplots as a fraction of figure width or height.
380-
wspace, hspace : float, optional
382+
wspace, hspace : float or None, optional
381383
Spacing between the subplots as a fraction of the average subplot
382384
width / height.
383385
"""
384-
for k, v in kwargs.items():
385-
if k in self._AllowedKeys:
386-
setattr(self, k, v)
387-
else:
388-
raise AttributeError(f"{k} is an unknown keyword")
386+
if left is not _UNSET:
387+
self.left = left
388+
if bottom is not _UNSET:
389+
self.bottom = bottom
390+
if right is not _UNSET:
391+
self.right = right
392+
if top is not _UNSET:
393+
self.top = top
394+
if wspace is not _UNSET:
395+
self.wspace = wspace
396+
if hspace is not _UNSET:
397+
self.hspace = hspace
398+
389399
for figmanager in _pylab_helpers.Gcf.figs.values():
390400
for ax in figmanager.canvas.figure.axes:
391401
if ax.get_subplotspec() is not None:

lib/matplotlib/gridspec.pyi

+11-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ from typing import Any, Literal, overload
33
from numpy.typing import ArrayLike
44
import numpy as np
55

6+
from matplotlib._api import _Unset
67
from matplotlib.axes import Axes
78
from matplotlib.backend_bases import RendererBase
89
from matplotlib.figure import Figure
@@ -78,7 +79,16 @@ class GridSpec(GridSpecBase):
7879
width_ratios: ArrayLike | None = ...,
7980
height_ratios: ArrayLike | None = ...,
8081
) -> None: ...
81-
def update(self, **kwargs: float | None) -> None: ...
82+
def update(
83+
self,
84+
*,
85+
left: float | None | _Unset = ...,
86+
bottom: float | None | _Unset = ...,
87+
right: float | None | _Unset = ...,
88+
top: float | None | _Unset = ...,
89+
wspace: float | None | _Unset = ...,
90+
hspace: float | None | _Unset = ...,
91+
) -> None: ...
8292
def locally_modified_subplot_params(self) -> list[str]: ...
8393
def tight_layout(
8494
self,

0 commit comments

Comments
 (0)