Skip to content

Commit 066e6ef

Browse files
committed
MNT: don't set method objects back onto the objects
If we are monkey-patching methods then we need to be sure that when we restore the original attribute we need to make sure we don't stick the method instance in place (which creates a circular reference).
1 parent 4356b67 commit 066e6ef

File tree

2 files changed

+56
-0
lines changed

2 files changed

+56
-0
lines changed

lib/matplotlib/cbook/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import types
2525
import warnings
2626
import weakref
27+
import inspect
2728

2829
import numpy as np
2930

@@ -2033,6 +2034,13 @@ def _setattr_cm(obj, **kwargs):
20332034
"""
20342035
sentinel = object()
20352036
origs = [(attr, getattr(obj, attr, sentinel)) for attr in kwargs]
2037+
# When you access a Python method the function is bound
2038+
# to the object at access time so you get a new instance
2039+
# of MethodType every time so replace them with sentinel
2040+
#
2041+
# https://docs.python.org/3/howto/descriptor.html#functions-and-methods
2042+
origs = [(attr, _ if not inspect.ismethod(_) else sentinel)
2043+
for attr, _ in origs]
20362044
try:
20372045
for attr, val in kwargs.items():
20382046
setattr(obj, attr, val)

lib/matplotlib/tests/test_cbook.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,3 +651,51 @@ def test_check_shape(target, test_shape):
651651
with pytest.raises(ValueError,
652652
match=error_pattern):
653653
cbook._check_shape(target, aardvark=data)
654+
655+
656+
def test_setattr_cm():
657+
class A:
658+
def __init__(self):
659+
self.aardvark = 'aardvark'
660+
self._p = 'p'
661+
662+
def meth(self):
663+
...
664+
665+
@property
666+
def prop(self):
667+
return self._p
668+
669+
@prop.setter
670+
def prop(self, val):
671+
self._p = val
672+
673+
a = A()
674+
# When you access a Python method the function is bound
675+
# to the object at access time so you get a new instance
676+
# of MethodType every time.
677+
#
678+
# https://docs.python.org/3/howto/descriptor.html#functions-and-methods
679+
assert a.meth is not a.meth
680+
# normal attribute should give you back the same
681+
# instance every time
682+
assert a.aardvark is a.aardvark
683+
# and our property happens to give the same instance every time
684+
assert a.prop is a.prop
685+
686+
with cbook._setattr_cm(
687+
a,
688+
aardvark='moose', meth=lambda: None, prop='b'
689+
):
690+
# because we have set a lambda, it is normal attribute access
691+
# and the same every time
692+
assert a.meth is a.meth
693+
assert a.aardvark is a.aardvark
694+
assert a.aardvark == 'moose'
695+
assert a.prop == 'b'
696+
697+
# check that we get different MethodType instances each time
698+
assert a.meth is not a.meth
699+
assert a.aardvark is a.aardvark
700+
assert a.aardvark == 'aardvark'
701+
assert a.prop is a.prop

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy