11
11
import contextlib
12
12
import functools
13
13
import gzip
14
+ import inspect
14
15
import itertools
15
16
import math
16
17
import operator
@@ -1961,6 +1962,10 @@ def _array_patch_perimeters(x, rstride, cstride):
1961
1962
def _setattr_cm (obj ,** kwargs ):
1962
1963
"""
1963
1964
Temporarily set some attributes; restore original state at context exit.
1965
+
1966
+ .. warning ::
1967
+
1968
+ This is not threadsafe.
1964
1969
"""
1965
1970
sentinel = object ()
1966
1971
origs = {}
@@ -1978,14 +1983,18 @@ def _setattr_cm(obj, **kwargs):
1978
1983
# we want to set the original value back.
1979
1984
if isinstance (cls_orig ,property ):
1980
1985
origs [attr ]= orig
1986
+
1987
+ # detect when we have Python Descriptors that supports
1988
+ # setting so we will need to restore it
1989
+ #
1990
+ # https://docs.python.org/3/howto/descriptor.html
1991
+ if hasattr (inspect .getattr_static (type (obj ),attr ),'__set__' ):
1992
+ origs [attr ]= orig
1993
+
1981
1994
# otherwise this is _something_ we are going to shadow at
1982
1995
# the instance dict level from higher up in the MRO. We
1983
1996
# are going to assume we can delattr(obj, attr) to clean
1984
- # up after ourselves. It is possible that this code will
1985
- # fail if used with a non-property custom descriptor which
1986
- # implements __set__ (and __delete__ does not act like a
1987
- # stack). However, this is an internal tool and we do not
1988
- # currently have any custom descriptors.
1997
+ # up after ourselves.
1989
1998
else :
1990
1999
origs [attr ]= sentinel
1991
2000