8000 Clarify error message for bad keyword arguments. · matplotlib/matplotlib@03eab63 · GitHub
[go: up one dir, main page]

Skip to content

Commit 03eab63

Browse files
committed
Clarify error message for bad keyword arguments.
`plot([], [], foo=42)` previously emitted ``` 'Line2D' object has no property 'foo' ``` which refers to the Matplotlib-specific concept of "properties". It now instead emits ``` Line2D.set() got an unexpected keyword argument 'foo' ``` which is modeled after the standard error message for unknown keyword arguments. (To maximize backcompat, the implementation goes through a new _internal_update, which does *not* error when the same prop is passed under different aliases. This could be changed later, but is not the goal of this PR.)
1 parent c0c3627 commit 03eab63

File tree

13 files changed

+74
-59
lines changed

13 files changed

+74
-59
lines changed

lib/matplotlib/artist.py

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,32 +1048,6 @@ def set_in_layout(self, in_layout):
10481048
"""
10491049
self._in_layout = in_layout
10501050

1051-
def update(self, props):
1052-
"""
1053-
Update this artist's properties from the dict *props*.
1054-
1055-
Parameters
1056-
----------
1057-
props : dict
1058-
"""
1059-
ret = []
1060-
with cbook._setattr_cm(self, eventson=False):
1061-
for k, v in props.items():
1062-
# Allow attributes we want to be able to update through
1063-
# art.update, art.set, setp.
1064-
if k == "axes":
1065-
ret.append(setattr(self, k, v))
1066-
else:
1067-
func = getattr(self, f"set_{k}", None)
1068-
if not callable(func):
1069-
raise AttributeError(f"{type(self).__name__!r} object "
1070-
f"has no property {k!r}")
1071-
ret.append(func(v))
1072-
if ret:
1073-
self.pchanged()
1074-
self.stale = True
1075-
return ret
1076-
10771051
def get_label(self):
10781052
"""Return the label used for this artist in the legend."""
10791053
return self._label
@@ -1161,12 +1135,53 @@ def properties(self):
11611135
"""Return a dictionary of all the properties of the artist."""
11621136
return ArtistInspector(self).properties()
11631137

1138+
def _update_props(self, caller, props):
1139+
"""Helper for `set` and `update`."""
1140+
errorfmt = { # Often called & only internal, so use plain dict access.
1141+
"update": "{.__name__!r} object has no property {!r}",
1142+
"set": "{.__name__}.set() got an unexpected keyword argument {!r}",
1143+
}[caller]
1144+
ret = []
1145+
with cbook._setattr_cm(self, eventson=False):
1146+
for k, v in props.items():
1147+
# Allow attributes we want to be able to update through
1148+
# art.update, art.set, setp.
1149+
if k == "axes":
1150+
ret.append(setattr(self, k, v))
1151+
else:
1152+
func = getattr(self, f"set_{k}", None)
1153+
if not callable(func):
1154+
raise AttributeError(errorfmt.format(type(self), k))
1155+
ret.append(func(v))
1156+
if ret:
1157+
self.pchanged()
1158+
self.stale = True
1159+
return ret
1160+
11641161
def set(self, **kwargs):
11651162
# docstring and signature are auto-generated via
11661163
# Artist._update_set_signature_and_docstring() at the end of the
11671164
# module.
1168-
kwargs = cbook.normalize_kwargs(kwargs, self)
1169-
return self.update(kwargs)
1165+
return self._update_props("set", cbook.normalize_kwargs(kwargs, self))
1166+
1167+
def _internal_update(self, **kwargs):
1168+
"""
1169+
Update artist properties without prenormalizing them, but generating
1170+
errors as if calling `set`.
1171+
1172+
The lack of prenormalization is to maintain backcompatibility.
1173+
"""
1174+
return self._update_props("set", kwargs)
1175+
1176+
def update(self, props):
1177+
"""
1178+
Update this artist's properties from the dict *props*.
1179+
1180+
Parameters
1181+
----------
1182+
props : dict
1183+
"""
1184+
return self._update_props("update", props)
11701185

11711186
@contextlib.contextmanager
11721187
def _cm_set(self, **kwargs):

lib/matplotlib/axes/_axes.py

Lines changed: 10 additions & 10 deletions
< 10000 tr class="diff-line-row">
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def set_title(self, label, fontdict=None, loc=None, pad=None, *, y=None,
166166
title.update(default)
167167
if fontdict is not None:
168168
title.update(fontdict)
169-
title.update(kwargs)
169+
title._internal_update(**kwargs)
170170
return title
171171

172172
def get_legend_handles_labels(self, legend_handler_map=None):
@@ -1064,7 +1064,7 @@ def hlines(self, y, xmin, xmax, colors=None, linestyles='solid',
10641064
lines = mcoll.LineCollection(masked_verts, colors=colors,
10651065
linestyles=linestyles, label=label)
10661066
self.add_collection(lines, autolim=False)
1067-
lines.update(kwargs)
1067+
lines._internal_update(**kwargs)
10681068

10691069
if len(y) > 0:
10701070
minx = min(xmin.min(), xmax.min())
@@ -1143,7 +1143,7 @@ def vlines(self, x, ymin, ymax, colors=None, linestyles='solid',
11431143
lines = mcoll.LineCollection(masked_verts, colors=colors,
11441144
linestyles=linestyles, label=label)
11451145
self.add_collection(lines, autolim=False)
1146-
lines.update(kwargs)
1146+
lines._internal_update(**kwargs)
11471147

11481148
if len(x) > 0:
11491149
minx = x.min()
@@ -1363,7 +1363,7 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1,
13631363
color=color,
13641364
linestyle=linestyle)
13651365
self.add_collection(coll, autolim=False)
1366-
coll.update(kwargs)
1366+
coll._internal_update(**kwargs)
13671367
colls.append(coll)
13681368

13691369
if len(positions) > 0:
@@ -2410,7 +2410,7 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
24102410
label='_nolegend_',
24112411
hatch=htch,
24122412
)
2413-
r.update(kwargs)
2413+
r._internal_update(**kwargs)
24142414
r.get_path()._interpolation_steps = 100
24152415
if orientation == 'vertical':
24162416
r.sticky_edges.y.append(b)
@@ -4502,7 +4502,7 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None,
45024502
collection.set_cmap(cmap)
45034503
collection.set_norm(norm)
45044504
collection._scale_norm(norm, vmin, vmax)
4505-
collection.update(kwargs)
4505+
collection._internal_update(**kwargs)
45064506

45074507
# Classic mode only:
45084508
# ensure there are margins to allow for the
@@ -4830,7 +4830,7 @@ def reduce_C_function(C: array) -> float
48304830
collection.set_cmap(cmap)
48314831
collection.set_norm(norm)
48324832
collection.set_alpha(alpha)
4833-
collection.update(kwargs)
4833+
collection._internal_update(**kwargs)
48344834
collection._scale_norm(norm, vmin, vmax)
48354835

48364836
corners = ((xmin, ymin), (xmax, ymax))
@@ -4882,7 +4882,7 @@ def reduce_C_function(C: array) -> float
48824882
bar.set_cmap(cmap)
48834883
bar.set_norm(norm)
48844884
bar.set_alpha(alpha)
4885-
bar.update(kwargs)
4885+
bar._internal_update(**kwargs)
48864886
bars.append(self.add_collection(bar, autolim=False))
48874887

48884888
collection.hbar, collection.vbar = bars
@@ -6763,11 +6763,11 @@ def hist(self, x, bins=None, range=None, density=False, weights=None,
67636763
for patch, lbl in itertools.zip_longest(patches, labels):
67646764
if patch:
67656765
p = patch[0]
6766-
p.update(kwargs)
6766+
p._internal_update(**kwargs)
67676767
if lbl is not None:
67686768
p.set_label(lbl)
67696769
for p in patch[1:]:
6770-
p.update(kwargs)
6770+
p._internal_update(**kwargs)
67716771
p.set_label('_nolegend_')
67726772

67736773
if nx == 1:

lib/matplotlib/axes/_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,7 @@ def __init__(self, fig, rect,
642642
if yscale:
643643
self.set_yscale(yscale)
644644

645-
self.update(kwargs)
645+
self._internal_update(**kwargs)
646646

647647
for name, axis in self._get_axis_map().items():
648648
axis.callbacks._pickled_cids.add(

lib/matplotlib/axis.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,7 +1635,7 @@ def set_label_text(self, label, fontdict=None, **kwargs):
16351635
self.label.set_text(label)
16361636
if fontdict is not None:
16371637
self.label.update(fontdict)
1638-
self.label.update(kwargs)
1638+
self.label._internal_update(**kwargs)
16391639
self.stale = True
16401640
return self.label
16411641

@@ -1818,10 +1818,10 @@ def set_ticklabels(self, ticklabels, *, minor=False, **kwargs):
18181818
tick_label = formatter(loc, pos)
18191819
# deal with label1
18201820
tick.label1.set_text(tick_label)
1821-
tick.label1.update(kwargs)
1821+
tick.label1._internal_update(**kwargs)
18221822
# deal with label2
18231823
tick.label2.set_text(tick_label)
1824-
tick.label2.update(kwargs)
1824+
tick.label2._internal_update(**kwargs)
18251825
# only return visible tick labels
18261826
if tick.label1.get_visible():
18271827
ret.append(tick.label1)

lib/matplotlib/collections.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ def __init__(self,
206206
self._offset_transform = offset_transform
207207

208208
self._path_effects = None
209-
self.update(kwargs)
209+
self._internal_update(**kwargs)
210210
self._paths = None
211211

212212
def get_paths(self):
@@ -1132,7 +1132,7 @@ def legend_elements(self, prop="colors", num="auto",
11321132

11331133
kw = dict(markeredgewidth=self.get_linewidths()[0],
11341134
alpha=self.get_alpha())
1135-
kw.update(kwargs)
1135+
kw._internal_update(**kwargs)
11361136

11371137
for val, lab in zip(values, label_values):
11381138
if prop == "colors":

lib/matplotlib/image.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ def __init__(self, ax,
273273

274274
self._imcache = None
275275

276-
self.update(kwargs)
276+
self._internal_update(**kwargs)
277277

278278
def __getstate__(self):
279279
# Save some space on the pickle by not saving the cache.
@@ -1215,7 +1215,7 @@ def __init__(self, ax,
12151215
**kwargs : `.Artist` properties
12161216
"""
12171217
super().__init__(ax, norm=norm, cmap=cmap)
1218-
self.update(kwargs)
1218+
self._internal_update(**kwargs)
12191219
if A is not None:
12201220
self.set_data(x, y, A)
12211221

@@ -1356,7 +1356,7 @@ def __init__(self, fig,
13561356
self.figure = fig
13571357
self.ox = offsetx
13581358
self.oy = offsety
1359-
self.update(kwargs)
1359+
self._internal_update(**kwargs)
13601360
self.magnification = 1.0
13611361

13621362
def get_extent(self):

lib/matplotlib/lines.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ def __init__(self, xdata, ydata,
391391

392392
# update kwargs before updating data to give the caller a
393393
# chance to init axes (and hence unit support)
394-
self.update(kwargs)
394+
self._internal_update(**kwargs)
395395
self.pickradius = pickradius
396396
self.ind_offset = 0
397397
if (isinstance(self._picker, Number) and

lib/matplotlib/offsetbox.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ class OffsetBox(martist.Artist):
193193
"""
194194
def __init__(self, *args, **kwargs):
195195
super().__init__(*args)
196-
self.update(kwargs)
196+
self._internal_update(**kwargs)
197197
# Clipping has not been implemented in the OffsetBox family, so
198198
# disable the clip flag for consistency. It can always be turned back
199199
# on to zero effect.
@@ -1359,7 +1359,7 @@ def __init__(self, offsetbox, xy,
13591359
if bboxprops:
13601360
self.patch.set(**bboxprops)
13611361

1362-
self.update(kwargs)
1362+
self._internal_update(**kwargs)
13631363

13641364
@property
13651365
def xyann(self):

lib/matplotlib/patches.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def __init__(self,
111111
self.set_joinstyle(joinstyle)
112112

113113
if len(kwargs):
114-
self.update(kwargs)
114+
self._internal_update(**kwargs)
115115

116116
def get_verts(self):
117117
"""

lib/matplotlib/projections/polar.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,7 +1307,7 @@ def set_thetagrids(self, angles, labels=None, fmt=None, **kwargs):
13071307
elif fmt is not None:
13081308
self.xaxis.set_major_formatter(mticker.FormatStrFormatter(fmt))
13091309
for t in self.xaxis.get_ticklabels():
1310-
t.update(kwargs)
1310+
t._internal_update(**kwargs)
13111311
return self.xaxis.get_ticklines(), self.xaxis.get_ticklabels()
13121312

13131313
def set_rgrids(self, radii, labels=None, angle=None, fmt=None, **kwargs):
@@ -1362,7 +1362,7 @@ def set_rgrids(self, radii, labels=None, angle=None, fmt=None, **kwargs):
13621362
angle = self.get_rlabel_position()
13631363
self.set_rlabel_position(angle)
13641364
for t in self.yaxis.get_ticklabels():
1365-
t.update(kwargs)
1365+
t._internal_update(**kwargs)
13661366
return self.yaxis.get_gridlines(), self.yaxis.get_ticklabels()
13671367

13681368
def format_coord(self, theta, r):

lib/matplotlib/pyplot.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1789,7 +1789,7 @@ def xticks(ticks=None, labels=None, **kwargs):
17891789
if labels is None:
17901790
labels = ax.get_xticklabels()
17911791
for l in labels:
1792-
l.update(kwargs)
1792+
l._internal_update(**kwargs)
17931793
else:
17941794
labels = ax.set_xticklabels(labels, **kwargs)
17951795

@@ -1849,7 +1849,7 @@ def yticks(ticks=None, labels=None, **kwargs):
18491849
if labels is None:
18501850
labels = ax.get_yticklabels()
18511851
for l in labels:
1852-
l.update(kwargs)
1852+
l._internal_update(**kwargs)
18531853
else:
1854< C2EE /td>1854
labels = ax.set_yticklabels(labels, **kwargs)
18551855

lib/matplotlib/table.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ def set_text_props(self, **kwargs):
184184
185185
%(Text:kwdoc)s
186186
"""
187-
self._text.update(kwargs)
187+
self._text._internal_update(**kwargs)
188188
self.stale = True
189189

190190
@property
@@ -315,7 +315,7 @@ def __init__(self, ax, loc=None, bbox=None, **kwargs):
315315
self._edges = None
316316
self._autoColumns = []
317317
self._autoFontsize = True
318-
self.update(kwargs)
318+
self._internal_update(**kwargs)
319319

320320
self.set_clip_on(False)
321321

lib/matplotlib/text.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def __init__(self,
173173
linespacing = 1.2 # Maybe use rcParam later.
174174
self._linespacing = linespacing
175175
self.set_rotation_mode(rotation_mode)
176-
self.update(kwargs)
176+
self._internal_update(**kwargs)
177177

178178
def update(self, kwargs):
179179
# docstring inherited
@@ -185,7 +185,7 @@ def update(self, kwargs):
185185
self.set_fontproperties(fontproperties)
186186
# Update bbox last, as it depends on font properties.
187187
bbox = kwargs.pop("bbox", sentinel)
188-
super().update(kwargs)
188+
super()._internal_update(**kwargs)
189189
if bbox is not sentinel:
190190
self.set_bbox(bbox)
191191

0 commit comments

Comments
 (0)
0