Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 17ccb3e

Browse files
committed
Make Artist.set() apply properties in the order in which they are given.
... with a deprecation period (see changelog). - This is consistent with Artist.update, and we don't reorder separate calls to set_color and set_edgecolor either (we can't really to that, indeed). - The _prop_order mechanism was a slightly complex machinery for a single use case (applying "color" first). - Writing `p.set(edgecolor="r", color="g")` seems a bit twisted anyways. Also rewrote (and simplified) normalize kwargs to make it *also* maintain kwarg order, as that's necessary to make the warning emitted in the correct cases.
1 parent c91d7ef commit 17ccb3e

File tree

3 files changed

+47
-45
lines changed

3 files changed

+47
-45
lines changed

doc/api/next_api_changes/deprecations.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,12 @@ The *clear_temp* parameter and attribute of `.FileMovieWriter` is
211211
deprecated. In the future, files placed in a temporary directory (using
212212
``frame_prefix=None``, the default) will be cleared; files placed elsewhere
213213
will not.
214+
215+
Reordering of parameters by `.Artist.set`
216+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
217+
In a future version, ``Artist.set`` will apply artist properties in the order
218+
in which they are given. This only affects the interaction between the
219+
*color*, *edgecolor*, *facecolor*, and, for `.Collection`\s, *alpha*
220+
properties: the *color* property now needs to be passed first in order not to
221+
override the other properties. This is consistent with e.g. `.Artist.update`,
222+
which did not reorder the properties passed to it.

lib/matplotlib/artist.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,6 @@ class Artist:
6262
"""
6363

6464
zorder = 0
65-
# order of precedence when bulk setting/updating properties
66-
# via update. The keys should be property names and the values
67-
# integers
68-
_prop_order = dict(color=-1)
6965

7066
def __init__(self):
7167
self._stale = True
@@ -1077,10 +1073,31 @@ def properties(self):
10771073
def set(self, **kwargs):
10781074
"""A property batch setter. Pass *kwargs* to set properties."""
10791075
kwargs = cbook.normalize_kwargs(kwargs, self)
1080-
props = OrderedDict(
1081-
sorted(kwargs.items(), reverse=True,
1082-
key=lambda x: (self._prop_order.get(x[0], 0), x[0])))
1083-
return self.update(props)
1076+
move_color_to_start = False
1077+
if "color" in kwargs:
1078+
keys = [*kwargs]
1079+
i_color = keys.index("color")
1080+
props = ["edgecolor", "facecolor"]
1081+
if any(tp.__module__ == "matplotlib.collections"
1082+
and tp.__name__ == "Collection"
1083+
for tp in type(self).__mro__):
1084+
props.append("alpha")
1085+
for other in props:
1086+
if other not in keys:
1087+
continue
1088+
i_other = keys.index(other)
1089+
if i_other < i_color:
1090+
move_color_to_start = True
1091+
cbook.warn_deprecated(
1092+
"3.3", message=f"You have passed the {other!r} kwarg "
1093+
"before the 'color' kwarg. Artist.set() currently "
1094+
"reorders the properties to apply 'color' first, but "
1095+
"this is deprecated since %(since)s and will be "
1096+
"removed %(removal)s; please pass 'color' first "
1097+
"instead.")
1098+
if move_color_to_start:
1099+
kwargs = {"color": kwargs.pop("color"), **kwargs}
1100+
return self.update(kwargs)
10841101

10851102
def findobj(self, match=None, include_self=True):
10861103
"""

lib/matplotlib/cbook/__init__.py

Lines changed: 13 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,43 +1771,19 @@ def normalize_kwargs(kw, alias_mapping=None, required=(), forbidden=(),
17711771
or isinstance(alias_mapping, Artist)):
17721772
alias_mapping = getattr(alias_mapping, "_alias_map", {})
17731773

1774-
# make a local so we can pop
1775-
kw = dict(kw)
1776-
# output dictionary
1777-
ret = dict()
1778-
1779-
# hit all alias mappings
1780-
for canonical, alias_list in alias_mapping.items():
1781-
1782-
# the alias lists are ordered from lowest to highest priority
1783-
# so we know to use the last value in this list
1784-
tmp = []
1785-
seen = []
1786-
for a in alias_list:
1787-
try:
1788-
tmp.append(kw.pop(a))
1789-
seen.append(a)
1790-
except KeyError:
1791-
pass
1792-
# if canonical is not in the alias_list assume highest priority
1793-
if canonical not in alias_list:
1794-
try:
1795-
tmp.append(kw.pop(canonical))
1796-
seen.append(canonical)
1797-
except KeyError:
1798-
pass
1799-
# if we found anything in this set of aliases put it in the return
1800-
# dict
1801-
if tmp:
1802-
ret[canonical] = tmp[-1]
1803-
if len(tmp) > 1:
1804-
raise TypeError("Got the following keyword arguments which "
1805-
"are aliases of one another: {}"
1806-
.format(", ".join(map(repr, seen))))
1807-
1808-
# at this point we know that all keys which are aliased are removed, update
1809-
# the return dictionary from the cleaned local copy of the input
1810-
ret.update(kw)
1774+
to_canonical = {alias: canonical
1775+
for canonical, alias_list in alias_mapping.items()
1776+
for alias in alias_list}
1777+
canonical_to_seen = {}
1778+
ret = {} # output dictionary
1779+
1780+
for k, v in kw.items():
1781+
canonical = to_canonical.get(k, k)
1782+
if canonical in canonical_to_seen:
1783+
raise TypeError(f"Got both {canonical_to_seen[canonical]!r} and "
1784+
f"{k!r}, which are aliases of one another")
1785+
canonical_to_seen[canonical] = k
1786+
ret[canonical] = v
18111787

18121788
fail_keys = [k for k in required if k not in ret]
18131789
if fail_keys:

0 commit comments

Comments
 (0)