Mutation of descriptor in__get__ or__set__ method.¶
ID: py/mutable-descriptorKind: problemSecurity severity: Severity: errorPrecision: very-highTags: - quality - reliability - correctnessQuery suites: - python-security-and-quality.qls
Click to see the query in the CodeQL repository
The descriptor protocol allows user programmable attribute access. The descriptor protocol is what enables class methods, static methods, properties andsuper().
Descriptor objects are class attributes which control the behavior of instance attributes. Consequently, a single descriptor is common to all instances of a class and should not be mutated when instance attributes are accessed.
Recommendation¶
Do not mutate the descriptor object, rather create a new object that contains the necessary state.
Example¶
In this example the descriptor classMutatingDescriptor stores a reference toobj in an attribute.
#This is prone to strange side effects and race conditions.classMutatingDescriptor(object):def__init__(self,func):self.my_func=funcdef__get__(self,obj,obj_type):#Modified state is visible to all instances of C that might call "show".self.my_obj=objreturnselfdef__call__(self,*args):returnself.my_func(self.my_obj,*args)defshow(obj):print(obj)classC(object):def__init__(self,value):self.value=valuedef__str__(self):return("C: "+str(self.value))show=MutatingDescriptor(show)c1=C(1)c1.show()c2=C(2)c2.show()c1_show=c1.showc2.showc1_show()#Outputs:#C: 1#C: 2#C: 2
In the following example, the descriptor classNonMutatingDescriptor returns a new object every time__get__ is called.
importtypes#Immutable version, which is safe to share.classNonMutatingDescriptor(object):def__init__(self,func):self.my_func=funcdef__get__(self,obj,obj_type):#Return a new object to each access.returntypes.MethodType(self.my_func,obj)defshow(obj):print(obj)classC(object):def__init__(self,value):self.value=valuedef__str__(self):return("C: "+str(self.value))show=NonMutatingDescriptor(show)c1=C(1)c1.show()c2=C(2)c2.show()c1_show=c1.showc2.showc1_show()#Outputs:#C: 1#C: 2#C: 1
References¶
Python Language Reference:Implementing Descriptors.
Mark Lutz.Learning Python, Section 30.6: Methods Are Objects: Bound or Unbound. O’Reilly 2013.
A real world example:NumPy Issue 5247.