Search code examples
pythonpython-3.xpartial-classesisinstance

Class instance fails isinstance check


Hi

I have some code on my CI failing (local runs do not fail). The problem is that class instance fails isinstance() check.

Code:

File: main.py

class MyController(SuperController):
    # Overrides default definition of get_variables_context()
    from my_options import get_variables_context

File: my_options.py

...
def get_variables_context(self: SuperController, **kwargs):
    from main import MyController
    self: MyController
    
    print(f"type(self) is {type(self)} (it {'IS' if (isinstance(self, MyController)) else 'IS NOT'} a subclass of MyController)")
    _super = super(MyController, self).get_variables_context(**kwargs) or dict()
    _super.update({ 'some_field': 'some value' })
    return _super

Got output and error:

type(self) is <class '__main__.MyController'> (it IS NOT a subclass of MyController)
Traceback (most recent call last):
  File "main.py", line 24, in <module>
    MyController.main(**params)
  File "/tmp/example/flow.py", line 391, in main
    _tests_suite, _, _ = self.prepare()
  File "/tmp/example/flow.py", line 359, in prepare
    context['variables_context'] = self.get_variables_context(**context)
  File "/tmp/example/my_options.py", line 81, in get_variables_context
    _super = super(SomeController, self).get_variables_context(**kwargs) or dict()
TypeError: super(type, obj): obj must be an instance or subtype of type

Solution

  • I've found the solution while investigating the root cause.

    In the local run, ...

    I actually call the python unittest which then calls main.py which then creates a class MyController which then calls my_options.py, and the class is added to the loaded module 'main'. Then, MyController.get_variables_context asks for the module 'main', which is already loaded, then for the class MyController in that module, so the same type instance is returned and type check succeeds.

    In the CI run, ...

    I call directly main.py with the argument "test" (which should create a controller and run all tests from it via unittest), so the class MyController is created inside module __main__. MyController.get_variables_context still asks for the MyController class in main.py, but the module 'main' is not loaded here, so python loads it, creates new class MyController, and then returns it.

    So, basically the answer is ...

    to move MyController from main.py to the other file, i.e. controller.py