Skip to content

Commit 4bf4115

Browse files
committed
MNT: handle all descriptors not just properties
As suggested by @eric-wieser
1 parent 9d5d10c commit 4bf4115

File tree

2 files changed

+41
-27
lines changed

2 files changed

+41
-27
lines changed

lib/matplotlib/cbook/__init__.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2030,6 +2030,10 @@ def _array_patch_perimeters(x, rstride, cstride):
20302030
def _setattr_cm(obj, **kwargs):
20312031
"""
20322032
Temporarily set some attributes; restore original state at context exit.
2033+
2034+
.. warning ::
2035+
2036+
This is not threadsafe.
20332037
"""
20342038
sentinel = object()
20352039

@@ -2038,14 +2042,28 @@ def _setattr_cm(obj, **kwargs):
20382042
# or are shadowing from something later in the resolution order.
20392043
def get_filtered_attr(obj, attr):
20402044
val = getattr(obj, attr, sentinel)
2045+
# this is the case where we are adding a new attribute, bail now
2046+
if val is sentinel:
2047+
return sentinel
20412048

2049+
# we are replacing something in the instance dict, we have to restore
2050+
# it so return the value
20422051
if attr in obj.__dict__:
20432052
return val
2044-
if isinstance(getattr(type(obj), attr), property):
2053+
2054+
# detect when we have Python Descriptors that supports
2055+
# setting so we will need to restore it
2056+
#
2057+
# https://docs.python.org/3/howto/descriptor.html
2058+
if hasattr(getattr(type(obj), attr), '__set__'):
20452059
return val
20462060

2061+
# in all other cases we are shadowing something from deeper in the
2062+
# resolution order by putting a value in the instance dict so we
2063+
# will want to remove it at the end.
20472064
return sentinel
20482065

2066+
# grab the original values or sentinel so we can clean up
20492067
origs = [(attr, get_filtered_attr(obj, attr)) for attr in kwargs]
20502068
try:
20512069
for attr, val in kwargs.items():

lib/matplotlib/tests/test_cbook.py

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -677,27 +677,29 @@ def prop(self, val):
677677
class B(A):
678678
...
679679

680-
a = B()
681-
# When you access a Python method the function is bound
682-
# to the object at access time so you get a new instance
683-
# of MethodType every time.
684-
#
685-
# https://docs.python.org/3/howto/descriptor.html#functions-and-methods
686-
assert a.meth is not a.meth
687-
# normal attribute should give you back the same
688-
# instance every time
689-
assert a.aardvark is a.aardvark
690-
# and our property happens to give the same instance every time
691-
assert a.prop is a.prop
692-
693-
assert a.cls_level is A.cls_level
694-
695-
assert a.override == 'override'
680+
def verify_pre_post_state(obj):
681+
# When you access a Python method the function is bound
682+
# to the object at access time so you get a new instance
683+
# of MethodType every time.
684+
#
685+
# https://docs.python.org/3/howto/descriptor.html#functions-and-methods
686+
assert obj.meth is not obj.meth
687+
# normal attribute should give you back the same
688+
# instance every time
689+
assert obj.aardvark is obj.aardvark
690+
assert a.aardvark == 'aardvark'
691+
# and our property happens to give the same instance every time
692+
assert obj.prop is obj.prop
693+
assert obj.cls_level is A.cls_level
694+
assert obj.override == 'override'
695+
assert not hasattr(obj, 'extra')
696696

697+
a = B()
698+
verify_pre_post_state(a)
697699
with cbook._setattr_cm(
698700
a,
699-
aardvark='moose', meth=lambda: None, prop='b', cls_level='bob',
700-
override='boo'
701+
aardvark='moose', meth=lambda: None, prop='b',
702+
cls_level='bob', override='boo', extra='extra'
701703
):
702704
# because we have set a lambda, it is normal attribute access
703705
# and the same every time
@@ -707,11 +709,5 @@ class B(A):
707709
assert a.prop == 'b'
708710
assert a.cls_level == 'bob'
709711
assert a.override == 'boo'
710-
711-
# check that we get different MethodType instances each time
712-
assert a.meth is not a.meth
713-
assert a.aardvark is a.aardvark
714-
assert a.aardvark == 'aardvark'
715-
assert a.prop is a.prop
716-
assert a.cls_level is A.cls_level
717-
assert a.override == 'override'
712+
assert a.extra == 'extra'
713+
verify_pre_post_state(a)

0 commit comments

Comments
 (0)