CodeQL queries 1.25
Skip to end of metadata
Go to start of metadata

Name: Missing call to __del__ during object destruction

Description: An omitted call to a super-class __del__ method may lead to class instances not being cleaned up properly.

ID: py/missing-call-to-delete

Kind: problem

Severity: error

Precision: high

Query: MissingCallToDel.ql
/**
 * @name Missing call to __del__ during object destruction
 * @description An omitted call to a super-class __del__ method may lead to class instances not being cleaned up properly.
 * @kind problem
 * @tags efficiency
 *       correctness
 * @problem.severity error
 * @sub-severity low
 * @precision high
 * @id py/missing-call-to-delete
 */

import python
import MethodCallOrder

from ClassObject self, FunctionObject missing
where
  missing_call_to_superclass_method(self, _, missing, "__del__") and
  not missing.neverReturns() and
  not self.failedInference() and
  not missing.isBuiltin()
select self,
  "Class " + self.getName() + " may not be cleaned up properly as $@ is not called during deletion.",
  missing, missing.descriptiveString()

Python, unlike statically typed languages such as Java, allows complete freedom when calling methods during object destruction. However, standard object-oriented principles apply to Python classes using deep inheritance hierarchies. Therefore the developer has responsibility for ensuring that objects are properly cleaned up when there are multiple __del__ methods that need to be called.

If the __del__ method of a superclass is not called during object destruction it is likely that that resources may be leaked.

A call to the __del__ method of a superclass during object destruction may be omitted:

  • When a subclass calls the __del__ method of the wrong class.
  • When a call to the __del__ method of one its base classes is omitted.
Recommendation

Either be careful to explicitly call the __del__ of the correct base class, or use super() throughout the inheritance hierarchy.

Alternatively refactor one or more of the classes to use composition rather than inheritance.

Example

In this example, explicit calls to __del__ are used, but SportsCar erroneously calls Vehicle.__del__. This is fixed in FixedSportsCar by calling Car.__del__.

class Vehicle(object):
    
    def __del__(self):
        recycle(self.base_parts)
        
class Car(Vehicle):
    
    def __del__(self):
        recycle(self.car_parts)
        Vehicle.__del__(self)
        
#Car.__del__ is missed out.
class SportsCar(Car, Vehicle):
    
    def __del__(self):
        recycle(self.sports_car_parts)
        Vehicle.__del__(self)
        
#Fix SportsCar by calling Car.__del__
class FixedSportsCar(Car, Vehicle):
    
    def __del__(self):
        recycle(self.sports_car_parts)
        Car.__del__(self)
        

References