1

I have a problem with debugging in PyCharm (maybe this problem is observable in other IDEs too):

If you take the following code:

class Test1:
    def __init__(self):
        self.arg1 = 'abc'
        self.arg2 = 'def'
        self.some_other_class = SomeOtherClass()
        return

    def __getattr__(self, item):
        print(item)
        if callable(getattr(self.some_other_class, 'blabla')):
            abc = 123
        return

x = Test1()

and my breakpoint is at self.arg1 = 'abc', the following happens:

Only in debugging mode there is a lookup for the instance variable __len__, which is not found --> then __getattr__ is called. (I see this because of the print(item)-Statement in __getattr__). Then self.some_other_class is looked up. This is not found: recursively dive into __getattr__ and so on and so on until RecursionError occurs.

Is there anything I can do against this behavior, because I need to debug?

Egirus Ornila
  • 1,234
  • 4
  • 14
  • 39
  • FWIW you can still debug, this recursion error does not stop the debugging session – DeepSpace Jan 26 '20 at 12:32
  • Yes, but the debugging behaves very strange (e.g. the pointer to the code-line is not correct anymore) and sometimes i get a real stackoverflow error from pycharm. – Egirus Ornila Jan 27 '20 at 08:24

1 Answers1

0

Short answer:

Either define a __len__ method or check whether 'some_other_class' is present in self.__dict__.keys().


Long answer:

The recursion occurs when

  1. you try to get a non-existent attribute from a class and
  2. you have a __getattr__ method in that class and
  3. within the __getattr__ method you try to get a non-existent attribute from the same class

The recursion error can for example be obtained in the following example, where self.some_other_class is not defined:

class Test1:
    def __init__(self):
        self.arg1 = 'abc'
        self.arg2 = 'def'
        return

    def __getattr__(self, item):
        print(item)
        self.some_other_class
        return

x = Test1()
x.test

When x.test is called, the __getattr__ method is called, which calls self.some_other_class, which again calls the __getattr__ method, which again calls self.some_other_class, etc.

Of course in your case the self.some_other_class is defined, and then there is no recursion:

class SomeOtherClass:
    def __init__(self):
        pass

class Test1:
    def __init__(self):
        self.arg1 = 'abc'
        self.arg2 = 'def'
        self.some_other_class = SomeOtherClass
        return

    def __getattr__(self, item):
        print(item)
        self.some_other_class
        return

x = Test1()
x.test

However, in the debug mode of PyCharm probably the __len__ attribute of the class is called after manually running a line in the file. In the example this means that after the first two lines in the Test1.__init__ method self.some_other_class is not defined yet, and also Test1.__len__ is not defined. Therefore calling the __len__ attribute will result in the recursion error.

There are two possible solutions:

  1. Define the __len__ method:

    class Test1:
        ...
        def __len__(self):
            return 1
    
  2. Check in __getattr__ whether the passed item is present in the class:

    class Test1:
        ...
        def __getattr__(self, item):
            print(item)
            if 'some_other_class' in self.__dict__:
                if hasattr(self.some_other_class, 'blabla')):
                    abc = 123
                return
    

    Note that just calling getattr(self.some_other_class, 'blabla') or hasattr(self.some_other_class, 'blabla') without the check in self.__dict__ also triggers the __getattr__ method.