Skip to content

MNT: unify code path of set, update, setp #5599

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 14, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions doc/api/api_changes/2015-12-01-TAC.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
`Artist.update` has return value
````````````````````````````````

The methods `matplotlib.artist.Artist.set`,
`matplotlib.Artist.update`, and the function `matplotlib.artist.setp`
now use a common codepath to look up how to update the given artist
properties (either using the setter methods or an attribute/property).

The behavior of `matplotlib.Artist.update` is slightly changed to
return a list of the returned values from the setter methods to avoid
changing the API of `matplotlib.Artist.set` and
`matplotlib.artist.setp`.

The keys passed into `matplotlib.Artist.update` are now converted to
lower case before being processed to match the behavior of
`matplotlib.Artist.set` and `matplotlib.artist.setp`. This should not
break any user code because there are no set methods with capitals in
the names, however going forward this puts a constraint on naming
properties.
89 changes: 48 additions & 41 deletions lib/matplotlib/artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
unicode_literals)

from matplotlib.externals import six
from collections import OrderedDict

import re
import warnings
Expand Down Expand Up @@ -82,6 +83,10 @@ class Artist(object):

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

def __init__(self):
self._stale = True
Expand Down Expand Up @@ -843,23 +848,43 @@ def update(self, props):
Update the properties of this :class:`Artist` from the
dictionary *prop*.
"""
store = self.eventson
self.eventson = False
changed = False

for k, v in six.iteritems(props):
if k in ['axes']:
setattr(self, k, v)
def _update_property(self, k, v):
"""sorting out how to update property (setter or setattr)

Parameters
----------
k : str
The name of property to update
v : obj
The value to assign to the property
Returns
-------
ret : obj or None
If using a `set_*` method return it's return, else None.
"""
k = k.lower()
# white list attributes we want to be able to update through
# art.update, art.set, setp
if k in {'axes'}:
return setattr(self, k, v)
else:
func = getattr(self, 'set_' + k, None)
if func is None or not six.callable(func):
raise AttributeError('Unknown property %s' % k)
func(v)
changed = True
self.eventson = store
if changed:
return func(v)

store = self.eventson
self.eventson = False
try:
ret = [_update_property(self, k, v)
for k, v in props.items()]
finally:
self.eventson = store

if len(ret):
self.pchanged()
self.stale = True
return ret

def get_label(self):
"""
Expand Down Expand Up @@ -919,23 +944,13 @@ def properties(self):
return ArtistInspector(self).properties()

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

"""
ret = []
for k, v in sorted(kwargs.items(), reverse=True):
k = k.lower()
funcName = "set_%s" % k
func = getattr(self, funcName, None)
if func is None:
raise TypeError('There is no %s property "%s"' %
(self.__class__.__name__, k))
ret.extend([func(v)])
return ret
return self.update(props)

def findobj(self, match=None, include_self=True):
"""
Expand Down Expand Up @@ -1445,26 +1460,18 @@ def setp(obj, *args, **kwargs):
if not cbook.iterable(obj):
objs = [obj]
else:
objs = cbook.flatten(obj)
objs = list(cbook.flatten(obj))

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

funcvals = []
# put args into ordereddict to maintain order
funcvals = OrderedDict()
for i in range(0, len(args) - 1, 2):
funcvals.append((args[i], args[i + 1]))
funcvals.extend(sorted(kwargs.items(), reverse=True))

ret = []
for o in objs:
for s, val in funcvals:
s = s.lower()
funcName = "set_%s" % s
func = getattr(o, funcName, None)
if func is None:
raise TypeError('There is no %s property "%s"' %
(o.__class__.__name__, s))
ret.extend([func(val)])
funcvals[args[i]] = args[i + 1]

ret = [o.update(funcvals) for o in objs]
ret.extend([o.set(**kwargs) for o in objs])
return [x for x in cbook.flatten(ret)]


Expand Down