diff --git a/lib/matplotlib/artist.py b/lib/matplotlib/artist.py index b6cf2d58f004..84ae41fabc57 100644 --- a/lib/matplotlib/artist.py +++ b/lib/matplotlib/artist.py @@ -1048,32 +1048,6 @@ def set_in_layout(self, in_layout): """ self._in_layout = in_layout - def update(self, props): - """ - Update this artist's properties from the dict *props*. - - Parameters - ---------- - props : dict - """ - ret = [] - with cbook._setattr_cm(self, eventson=False): - for k, v in props.items(): - # Allow attributes we want to be able to update through - # art.update, art.set, setp. - if k == "axes": - ret.append(setattr(self, k, v)) - else: - func = getattr(self, f"set_{k}", None) - if not callable(func): - raise AttributeError(f"{type(self).__name__!r} object " - f"has no property {k!r}") - ret.append(func(v)) - if ret: - self.pchanged() - self.stale = True - return ret - def get_label(self): """Return the label used for this artist in the legend.""" return self._label @@ -1161,12 +1135,58 @@ def properties(self): """Return a dictionary of all the properties of the artist.""" return ArtistInspector(self).properties() + def _update_props(self, props, errfmt): + """ + Helper for `.Artist.set` and `.Artist.update`. + + *errfmt* is used to generate error messages for invalid property + names; it get formatted with ``type(self)`` and the property name. + """ + ret = [] + with cbook._setattr_cm(self, eventson=False): + for k, v in props.items(): + # Allow attributes we want to be able to update through + # art.update, art.set, setp. + if k == "axes": + ret.append(setattr(self, k, v)) + else: + func = getattr(self, f"set_{k}", None) + if not callable(func): + raise AttributeError( + errfmt.format(cls=type(self), prop_name=k)) + ret.append(func(v)) + if ret: + self.pchanged() + self.stale = True + return ret + + def update(self, props): + """ + Update this artist's properties from the dict *props*. + + Parameters + ---------- + props : dict + """ + return self._update_props( + props, "{cls.__name__!r} object has no property {prop_name!r}") + + def _internal_update(self, kwargs): + """ + Update artist properties without prenormalizing them, but generating + errors as if calling `set`. + + The lack of prenormalization is to maintain backcompatibility. + """ + return self._update_props( + kwargs, "{cls.__name__}.set() got an unexpected keyword argument " + "{prop_name!r}") + def set(self, **kwargs): # docstring and signature are auto-generated via # Artist._update_set_signature_and_docstring() at the end of the # module. - kwargs = cbook.normalize_kwargs(kwargs, self) - return self.update(kwargs) + return self._internal_update(cbook.normalize_kwargs(kwargs, self)) @contextlib.contextmanager def _cm_set(self, **kwargs): diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index ee38cc91ab83..3589255d2333 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -166,7 +166,7 @@ def set_title(self, label, fontdict=None, loc=None, pad=None, *, y=None, title.update(default) if fontdict is not None: title.update(fontdict) - title.update(kwargs) + title._internal_update(kwargs) return title def get_legend_handles_labels(self, legend_handler_map=None): @@ -1064,7 +1064,7 @@ def hlines(self, y, xmin, xmax, colors=None, linestyles='solid', lines = mcoll.LineCollection(masked_verts, colors=colors, linestyles=linestyles, label=label) self.add_collection(lines, autolim=False) - lines.update(kwargs) + lines._internal_update(kwargs) if len(y) > 0: minx = min(xmin.min(), xmax.min()) @@ -1143,7 +1143,7 @@ def vlines(self, x, ymin, ymax, colors=None, linestyles='solid', lines = mcoll.LineCollection(masked_verts, colors=colors, linestyles=linestyles, label=label) self.add_collection(lines, autolim=False) - lines.update(kwargs) + lines._internal_update(kwargs) if len(x) > 0: minx = x.min() @@ -1363,7 +1363,7 @@ def eventplot(self, positions, orientation='horizontal', lineoffsets=1, color=color, linestyle=linestyle) self.add_collection(coll, autolim=False) - coll.update(kwargs) + coll._internal_update(kwargs) colls.append(coll) if len(positions) > 0: @@ -2410,7 +2410,7 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center", label='_nolegend_', hatch=htch, ) - r.update(kwargs) + r._internal_update(kwargs) r.get_path()._interpolation_steps = 100 if orientation == 'vertical': 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, collection.set_cmap(cmap) collection.set_norm(norm) collection._scale_norm(norm, vmin, vmax) - collection.update(kwargs) + collection._internal_update(kwargs) # Classic mode only: # ensure there are margins to allow for the @@ -4830,7 +4830,7 @@ def reduce_C_function(C: array) -> float collection.set_cmap(cmap) collection.set_norm(norm) collection.set_alpha(alpha) - collection.update(kwargs) + collection._internal_update(kwargs) collection._scale_norm(norm, vmin, vmax) corners = ((xmin, ymin), (xmax, ymax)) @@ -4882,7 +4882,7 @@ def reduce_C_function(C: array) -> float bar.set_cmap(cmap) bar.set_norm(norm) bar.set_alpha(alpha) - bar.update(kwargs) + bar._internal_update(kwargs) bars.append(self.add_collection(bar, autolim=False)) collection.hbar, collection.vbar = bars @@ -6763,11 +6763,11 @@ def hist(self, x, bins=None, range=None, density=False, weights=None, for patch, lbl in itertools.zip_longest(patches, labels): if patch: p = patch[0] - p.update(kwargs) + p._internal_update(kwargs) if lbl is not None: p.set_label(lbl) for p in patch[1:]: - p.update(kwargs) + p._internal_update(kwargs) p.set_label('_nolegend_') if nx == 1: diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 64b34f3dc3cd..77e829912964 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -642,7 +642,7 @@ def __init__(self, fig, rect, if yscale: self.set_yscale(yscale) - self.update(kwargs) + self._internal_update(kwargs) for name, axis in self._get_axis_map().items(): axis.callbacks._pickled_cids.add( diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 27a2ae98096d..790154a148ac 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1818,10 +1818,10 @@ def set_ticklabels(self, ticklabels, *, minor=False, **kwargs): tick_label = formatter(loc, pos) # deal with label1 tick.label1.set_text(tick_label) - tick.label1.update(kwargs) + tick.label1._internal_update(kwargs) # deal with label2 tick.label2.set_text(tick_label) - tick.label2.update(kwargs) + tick.label2._internal_update(kwargs) # only return visible tick labels if tick.label1.get_visible(): ret.append(tick.label1) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index a7c8b631c26d..1eda802a0c73 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -206,7 +206,7 @@ def __init__(self, self._offset_transform = offset_transform self._path_effects = None - self.update(kwargs) + self._internal_update(kwargs) self._paths = None def get_paths(self): diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 8e89a3b065fe..ed4f70b39d0e 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -273,7 +273,7 @@ def __init__(self, ax, self._imcache = None - self.update(kwargs) + self._internal_update(kwargs) def __getstate__(self): # Save some space on the pickle by not saving the cache. @@ -1215,7 +1215,7 @@ def __init__(self, ax, **kwargs : `.Artist` properties """ super().__init__(ax, norm=norm, cmap=cmap) - self.update(kwargs) + self._internal_update(kwargs) if A is not None: self.set_data(x, y, A) @@ -1356,7 +1356,7 @@ def __init__(self, fig, self.figure = fig self.ox = offsetx self.oy = offsety - self.update(kwargs) + self._internal_update(kwargs) self.magnification = 1.0 def get_extent(self): diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index c9254a1bcf33..d1b7b6b3b61f 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -391,7 +391,7 @@ def __init__(self, xdata, ydata, # update kwargs before updating data to give the caller a # chance to init axes (and hence unit support) - self.update(kwargs) + self._internal_update(kwargs) self.pickradius = pickradius self.ind_offset = 0 if (isinstance(self._picker, Number) and diff --git a/lib/matplotlib/offsetbox.py b/lib/matplotlib/offsetbox.py index 41c2b047e13d..161e0e31718c 100644 --- a/lib/matplotlib/offsetbox.py +++ b/lib/matplotlib/offsetbox.py @@ -193,7 +193,7 @@ class OffsetBox(martist.Artist): """ def __init__(self, *args, **kwargs): super().__init__(*args) - self.update(kwargs) + self._internal_update(kwargs) # Clipping has not been implemented in the OffsetBox family, so # disable the clip flag for consistency. It can always be turned back # on to zero effect. @@ -1359,7 +1359,7 @@ def __init__(self, offsetbox, xy, if bboxprops: self.patch.set(**bboxprops) - self.update(kwargs) + self._internal_update(kwargs) @property def xyann(self): diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index 894717dcdd3e..0b6435ce0802 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -111,7 +111,7 @@ def __init__(self, self.set_joinstyle(joinstyle) if len(kwargs): - self.update(kwargs) + self._internal_update(kwargs) def get_verts(self): """ diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index 924dd22fbe61..c9f7441eea88 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -1307,7 +1307,7 @@ def set_thetagrids(self, angles, labels=None, fmt=None, **kwargs): elif fmt is not None: self.xaxis.set_major_formatter(mticker.FormatStrFormatter(fmt)) for t in self.xaxis.get_ticklabels(): - t.update(kwargs) + t._internal_update(kwargs) return self.xaxis.get_ticklines(), self.xaxis.get_ticklabels() 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): angle = self.get_rlabel_position() self.set_rlabel_position(angle) for t in self.yaxis.get_ticklabels(): - t.update(kwargs) + t._internal_update(kwargs) return self.yaxis.get_gridlines(), self.yaxis.get_ticklabels() def format_coord(self, theta, r): diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 31586b5ec1bb..1b36d5188e1c 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -1789,7 +1789,7 @@ def xticks(ticks=None, labels=None, **kwargs): if labels is None: labels = ax.get_xticklabels() for l in labels: - l.update(kwargs) + l._internal_update(kwargs) else: labels = ax.set_xticklabels(labels, **kwargs) @@ -1849,7 +1849,7 @@ def yticks(ticks=None, labels=None, **kwargs): if labels is None: labels = ax.get_yticklabels() for l in labels: - l.update(kwargs) + l._internal_update(kwargs) else: labels = ax.set_yticklabels(labels, **kwargs) diff --git a/lib/matplotlib/table.py b/lib/matplotlib/table.py index 15ac6e4ae75a..086872e69088 100644 --- a/lib/matplotlib/table.py +++ b/lib/matplotlib/table.py @@ -184,7 +184,7 @@ def set_text_props(self, **kwargs): %(Text:kwdoc)s """ - self._text.update(kwargs) + self._text._internal_update(kwargs) self.stale = True @property @@ -315,7 +315,7 @@ def __init__(self, ax, loc=None, bbox=None, **kwargs): self._edges = None self._autoColumns = [] self._autoFontsize = True - self.update(kwargs) + self._internal_update(kwargs) self.set_clip_on(False)