Skip to content

Commit 16f4895

Browse files
committed
Merge pull request #5599 from tacaswell/mnt_unify_artist_update-set
MNT: unify code path of set, update, setp
2 parents d31a156 + 1905607 commit 16f4895

File tree

2 files changed

+67
-41
lines changed

2 files changed

+67
-41
lines changed
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
`Artist.update` has return value
2+
````````````````````````````````
3+
4+
The methods `matplotlib.artist.Artist.set`,
5+
`matplotlib.Artist.update`, and the function `matplotlib.artist.setp`
6+
now use a common codepath to look up how to update the given artist
7+
properties (either using the setter methods or an attribute/property).
8+
9+
The behavior of `matplotlib.Artist.update` is slightly changed to
10+
return a list of the returned values from the setter methods to avoid
11+
changing the API of `matplotlib.Artist.set` and
12+
`matplotlib.artist.setp`.
13+
14+
The keys passed into `matplotlib.Artist.update` are now converted to
15+
lower case before being processed to match the behavior of
16+
`matplotlib.Artist.set` and `matplotlib.artist.setp`. This should not
17+
break any user code because there are no set methods with capitals in
18+
the names, however going forward this puts a constraint on naming
19+
properties.

lib/matplotlib/artist.py

+48-41
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
unicode_literals)
33

44
from matplotlib.externals import six
5+
from collections import OrderedDict
56

67
import re
78
import warnings
@@ -82,6 +83,10 @@ class Artist(object):
8283

8384
aname = 'Artist'
8485
zorder = 0
86+
# order of precedence when bulk setting/updating properties
87+
# via update. The keys should be property names and the values
88+
# integers
89+
_prop_order = dict(color=-1)
8590

8691
def __init__(self):
8792
self._stale = True
@@ -845,23 +850,43 @@ def update(self, props):
845850
Update the properties of this :class:`Artist` from the
846851
dictionary *prop*.
847852
"""
848-
store = self.eventson
849-
self.eventson = False
850-
changed = False
851-
852-
for k, v in six.iteritems(props):
853-
if k in ['axes']:
854-
setattr(self, k, v)
853+
def _update_property(self, k, v):
854+
"""sorting out how to update property (setter or setattr)
855+
856+
Parameters
857+
----------
858+
k : str
859+
The name of property to update
860+
v : obj
861+
The value to assign to the property
862+
Returns
863+
-------
864+
ret : obj or None
865+
If using a `set_*` method return it's return, else None.
866+
"""
867+
k = k.lower()
868+
# white list attributes we want to be able to update through
869+
# art.update, art.set, setp
870+
if k in {'axes'}:
871+
return setattr(self, k, v)
855872
else:
856873
func = getattr(self, 'set_' + k, None)
857874
if func is None or not six.callable(func):
858875
raise AttributeError('Unknown property %s' % k)
859-
func(v)
860-
changed = True
861-
self.eventson = store
862-
if changed:
876+
return func(v)
877+
878+
store = self.eventson
879+
self.eventson = False
880+
try:
881+
ret = [_update_property(self, k, v)
882+
for k, v in props.items()]
883+
finally:
884+
self.eventson = store
885+
886+
if len(ret):
863887
self.pchanged()
864888
self.stale = True
889+
return ret
865890

866891
def get_label(self):
867892
"""
@@ -1014,23 +1039,13 @@ def properties(self):
10141039
return ArtistInspector(self).properties()
10151040

10161041
def set(self, **kwargs):
1042+
"""A property batch setter. Pass *kwargs* to set properties.
10171043
"""
1018-
A property batch setter. Pass *kwargs* to set properties.
1019-
Will handle property name collisions (e.g., if both
1020-
'color' and 'facecolor' are specified, the property
1021-
with higher priority gets set last).
1044+
props = OrderedDict(
1045+
sorted(kwargs.items(), reverse=True,
1046+
key=lambda x: (self._prop_order.get(x[0], 0), x[0])))
10221047

1023-
"""
1024-
ret = []
1025-
for k, v in sorted(kwargs.items(), reverse=True):
1026-
k = k.lower()
1027-
funcName = "set_%s" % k
1028-
func = getattr(self, funcName, None)
1029-
if func is None:
1030-
raise TypeError('There is no %s property "%s"' %
1031-
(self.__class__.__name__, k))
1032-
ret.extend([func(v)])
1033-
return ret
1048+
return self.update(props)
10341049

10351050
def findobj(self, match=None, include_self=True):
10361051
"""
@@ -1540,26 +1555,18 @@ def setp(obj, *args, **kwargs):
15401555
if not cbook.iterable(obj):
15411556
objs = [obj]
15421557
else:
1543-
objs = cbook.flatten(obj)
1558+
objs = list(cbook.flatten(obj))
15441559

15451560
if len(args) % 2:
15461561
raise ValueError('The set args must be string, value pairs')
15471562

1548-
funcvals = []
1563+
# put args into ordereddict to maintain order
1564+
funcvals = OrderedDict()
15491565
for i in range(0, len(args) - 1, 2):
1550-
funcvals.append((args[i], args[i + 1]))
1551-
funcvals.extend(sorted(kwargs.items(), reverse=True))
1552-
1553-
ret = []
1554-
for o in objs:
1555-
for s, val in funcvals:
1556-
s = s.lower()
1557-
funcName = "set_%s" % s
1558-
func = getattr(o, funcName, None)
1559-
if func is None:
1560-
raise TypeError('There is no %s property "%s"' %
1561-
(o.__class__.__name__, s))
1562-
ret.extend([func(val)])
1566+
funcvals[args[i]] = args[i + 1]
1567+
1568+
ret = [o.update(funcvals) for o in objs]
1569+
ret.extend([o.set(**kwargs) for o in objs])
15631570
return [x for x in cbook.flatten(ret)]
15641571

15651572

0 commit comments

Comments
 (0)