Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ipdb.set_trace() breaks inspect #85

Comments

@sthompson0
Copy link

I originally filed here: ipython/ipython#8671

If a script attempts to use inspect.getfile() on a class that was defined in a script file after set_trace() is called an exception is raised. Here's the traceback and the demo script.

I discovered that ipython is replacing sys.modules['main'] at some point (didn't get as far as pinpointing an exact line). This object ipython puts in there doesn't have attributes inspect is looking for which leads to the exception. It seems ipython could clean up after itself when set_trace() exits or copy those attributes to the replaced item.

I'd really like to get a work around so I can make a local modification until this fix goes out.

Version info
Python 2.7.10

pip freeze | grep ipython
ipython==3.2.1
pip freeze | grep ipdb
ipdb==0.8.1

Traceback

> /Users/username/folder/test.py(10)test()
      9         ipdb.set_trace()
---> 10         inspect.getfile(self.__class__)
     11
ipdb> c
E
======================================================================
ERROR: test (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_test.py", line 10, in test
    inspect.getfile(self.__class__)
  File "/Users/username/homebrew/Cellar/python/2.7.10_2/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 408, in getfile
raise TypeError('{!r} is a built-in class'.format(object))
TypeError: <module '__main__' (built-in)> is a built-in class
----------------------------------------------------------------------
Ran 1 test in 1.268s

FAILED (errors=1)

Script for reproduction

import inspect
import unittest
import ipdb

class Test(unittest.TestCase):
    def test(self):
        inspect.getfile(self.__class__)
        ipdb.set_trace()
        inspect.getfile(self.__class__)

def main():
    unittest.main()

if __name__ == '__main__':
    main()
@takluyver
Copy link
Contributor

Bringing across my most useful comment after I had dug into this issue:

Sorry, fell off the radar. I've just dug into it, and I think the easiest sensible fix would be in ipdb. I fixed it in my local copy by making the set_trace() function look like this:

def set_trace(frame=None):
    update_stdout()
    wrap_sys_excepthook()
    if frame is None:
        frame = sys._getframe().f_back
    p = Pdb(def_colors)
    p.set_trace(frame)
    p.shell.restore_sys_module_state()

The last line is the key bit - that will put sys.modules['__main__'] back to its previous value.

Perhaps we shouldn't be replacing the __main__ module when InteractiveShell is merely instantiated, but that would require much more delicate changes to address.

@sthompson0
Copy link
Author

ping @gotcha Any chance to look at this soon? Thanks!

@gotcha
Copy link
Owner

gotcha commented Apr 12, 2016

Released in https://pypi.python.org/pypi/ipdb/0.9.1

@gotcha gotcha closed this as completed Apr 12, 2016
@hawkinchina
Copy link

hawkinchina commented Jan 10, 2020

I met the same problem. my testcase was very simple, just set ipdb.set_trace() in the sub thread. i am not sure ipdb support multi-threaded debugging ?
eg. in the main, we get two thread by threading.Thread(target = printNum, args=(13, ), name = "thread_1")

if __name__ == "__main__":
 
    th1 = threading.Thread(target=printNum, args=(13,), name="thread_1")
    th2 = threading.Thread(target=printNum, args=(10,), name="thread_2")
   
    th1.start()
    th2.start()

    # wait until the sub thread return.
    th1.join()
    th2.join()
    print("{0} ..the end...".format(threading.currentThread().getName()))
   

the function for the thread:

def printNum(idx):
    for num in range(idx): 
        # print the name of current running thread.
        ipdb().set_trace()   # --->failed.
        print("{0}\tnum={1}".format(threading.current_thread().getName(), num))
        logging.debug("(%s, %d)" % (threading.current_thread().getName(), num))

        delay = math.ceil(random.random()*2)
        time.sleep(delay)

when debugging, it was failed.

python test.py

log:
File "/Users/jack/.pyenv/versions/3.7.3/lib/python3.7/pdb.py", line 321, in _cmdloop
self.cmdloop()
File "/Users/jack/.pyenv/versions/3.7.3/lib/python3.7/site-packages/IPython/terminal/debugger.py", line 97, in cmdloop
line = self.pt_app.prompt() # reset_current_buffer=True)
File "/Users/jack/.pyenv/versions/3.7.3/lib/python3.7/site-packages/prompt_toolkit/shortcuts/prompt.py", line 986, in prompt
return self.app.run()
File "/Users/jack/.pyenv/versions/3.7.3/lib/python3.7/site-packages/prompt_toolkit/application/application.py", line 788, in run
return get_event_loop().run_until_complete(self.run_async(pre_run=pre_run))
File "/Users/jack/.pyenv/versions/3.7.3/lib/python3.7/asyncio/events.py", line 644, in get_event_loop
% threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'thread_1'.

@gotcha
Copy link
Owner

gotcha commented Jan 10, 2020

@hawkinchina You seem to bring same issue as #184.

@hawkinchina
Copy link

I find some info. about debugging with multi-thread.
https://stackoverflow.com/questions/4716533/how-to-attach-debugger-to-a-python-subproccess#
at least, it work. but I hope ipdb or pdb can merge this issue.

import sys
import pdb

class ForkedPdb(pdb.Pdb):
"""A Pdb subclass that may be used
from a forked multiprocessing child

"""
def interaction(self, *args, **kwargs):
    _stdin = sys.stdin
    try:
        sys.stdin = open('/dev/stdin')
        pdb.Pdb.interaction(self, *args, **kwargs)
    finally:
        sys.stdin = _stdin

Use it the same way you might use the classic Pdb:

ForkedPdb().set_trace()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment