Werid bug that you cannot even debug in Python

Recently I took over an existing project. There is some newly added feature and I started to debug using ipdb. So everything went well--I input "\n" to see what happens when executed to next line. But then all of a sudden this command does not work anymore--seems the debugger just breaks and all of my subsequent breakpoints did not pause the program execution.

So I thought maybe it is a bug in ipdb. Then I opened PyCharm and this time the debugger did not work at all. I tried pudb and pdb, nothing works--the debugger just fail at the certain step.

So I traced the program manually and found there is a misuse of `__getattribute__`. I simplify the code to the following:

~~~python

class TestAttribute(object):

   """docstring for TestAttribute"""

   def __init__(self, is_testing=False):

       super(TestAttribute, self).__init__()

       self.is_testing = is_testing

   def __getattribute__(self, name):

       # print(name)

       try:

           # the line below will trigger the recursion error

           if self.is_testing:

               name = name.upper()

           return super(TestAttribute, self).__getattribute__(name)

       except AttributeError:

           return None

       except Exception:

           # this line is added by me to see the output

           import traceback; traceback.print_exc();

           return None

   def __getitem__(self, name):

       return self.__getattribute__(name)

   def __setitem__(self, name, val):

       return self.__setattr__(name, val)

   def __setattr__(self, name, val):

       # so this func will be called in __init__ and will

       # enter __getattribute__

       if self.is_testing:

           name = name.lower()

       super(TestAttribute, self).__setattr__(name, val)

if __name__ == '__main__':

   ttt = TestAttribute()

   import pdb; pdb.set_trace()

   ttt.k = 1

   print('test done')

   print('test done again')

   print('test done again')

   print('test done again')

~~~

Output as below:

~~~

Traceback (most recent call last):

 File "test_getattribute.py", line 10, in __getattribute__

Traceback (most recent call last):

 File "test_getattribute.py", line 10, in __getattribute__

   if self.is_testing:

 File "test_getattribute.py", line 16, in __getattribute__

   import traceback; traceback.print_exc();

 File "/usr/lib/python2.7/traceback.py", line 232, in print_exc

   print_exception(etype, value, tb, limit, file)

 File "/usr/lib/python2.7/traceback.py", line 125, in print_exception

   print_tb(tb, limit, file)

 File "/usr/lib/python2.7/traceback.py", line 69, in print_tb

   line = linecache.getline(filename, lineno, f.f_globals)

 File "/home/jgu/repos/.venv/lib/python2.7/linecache.py", line 14, in getline

   lines = getlines(filename, module_globals)

 File "/home/jgu/repos/.venv/lib/python2.7/linecache.py", line 40, in getlines

   return updatecache(filename, module_globals)

RuntimeError: maximum recursion depth exceeded

> /home/jgu/repos/dat_cs/test_getattribute.py(34)()

-> ttt.k = 1

(Pdb) n

Traceback (most recent call last):

 File "test_getattribute.py", line 10, in __getattribute__

   if self.is_testing:

 File "test_getattribute.py", line 7, in __getattribute__

   def __getattribute__(self, name):

 File "/usr/lib/python2.7/bdb.py", line 50, in trace_dispatch

   return self.dispatch_call(frame, arg)

 File "/usr/lib/python2.7/bdb.py", line 76, in dispatch_call

   if not (self.stop_here(frame) or self.break_anywhere(frame)):

 File "/usr/lib/python2.7/bdb.py", line 147, in break_anywhere

   return self.canonic(frame.f_code.co_filename) in self.breaks

 File "/usr/lib/python2.7/bdb.py", line 29, in canonic

   if filename == "<" + filename[1:-1] + ">":

RuntimeError: maximum recursion depth exceeded in cmp

test done

test done again

test done again

test done again

~~~

So the normal behaviour of pdb should be: after I input "n", it should go to next line and wait for my further instruction. However, in this example, the program did not pause at next line but instead continued the execution until its end.

As you can see from the output, there is RuntimeError saying max recursion depth exceeded. This happens because `__getattribute__` is used whenever some lookup happens, so `self.is_testing` will invoke its `__getattribute__`. And notice that the last line in max recursion error is in bdb--file in which there is a base class for pdb. And if we dig deeper, we find that it uses `sys.settrace` to debug--in fact that is what is `sys.settrace` for. And if any exception happens in tracefunc, [sys.settrace](https://docs.python.org/2/library/sys.html#sys.settrace) will stop working (Any exception that propagates out of the trace function disables it). Therefore pdb will not work anymore as `sys.settrace` stopped working and tracefunc will not be invoked anymore.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • **2014真题Directions:Read the following text. Choose the be...
    又是夜半惊坐起阅读 13,495评论 0 23
  • 1,我:来了一项任务,请问不知道是否方便提供一下表格哈?对方:如果方便的话,请来办公室共同协商。2,我:为什么呢?...
    我爱穿马甲阅读 1,207评论 0 0
  • 最近,相关写作的手机软件突然间风起云涌。而文字价值也日益被开发者们作为口号标语打出。也许真会有一天人人都写作,全民...
    浮于浮生阅读 5,489评论 0 7
  • 健身,有规律且健康的对身体有效的运动,我们称作为健身。健身大致分两类,有氧健身和无氧健身,其实还存在半有氧半无氧运...
    猫云七阅读 4,408评论 1 6

友情链接更多精彩内容