ipdb
ipdb copied to clipboard
Trouble with asyncio and ipdb
I'm finding trouble using ipdb in this scenario. Not sure if I'm missing something here
import asyncio
import ipdb
async def main():
await asyncio.sleep(1)
ipdb.set_trace()
asyncio.run(main())
The error thrown: $ python ipd.py
--Return--
None
> /Users/om.saran/wspace/buffer_manager/ipd.py(6)main()
5 await asyncio.sleep(1)
----> 6 ipdb.set_trace()
7
ipdb>
Traceback (most recent call last):
File "ipd.py", line 8, in <module>
asyncio.run(main())
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/runners.py", line 43, in run
return loop.run_until_complete(main)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 599, in run_until_complete
self.run_forever()
File "ipd.py", line 6, in main
ipdb.set_trace()
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/bdb.py", line 92, in trace_dispatch
return self.dispatch_return(frame, arg)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/bdb.py", line 151, in dispatch_return
self.user_return(frame, arg)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/pdb.py", line 293, in user_return
self.interaction(frame, None)
File "/Users/om.saran/wspace/buffer_manager/env/lib/python3.8/site-packages/IPython/core/debugger.py", line 291, in interaction
OldPdb.interaction(self, frame, traceback)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/pdb.py", line 356, in interaction
self._cmdloop()
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/pdb.py", line 321, in _cmdloop
self.cmdloop()
File "/Users/om.saran/wspace/buffer_manager/env/lib/python3.8/site-packages/IPython/terminal/debugger.py", line 114, in cmdloop
line = self.pt_app.prompt()
File "/Users/om.saran/wspace/buffer_manager/env/lib/python3.8/site-packages/prompt_toolkit/shortcuts/prompt.py", line 986, in prompt
return self.app.run()
File "/Users/om.saran/wspace/buffer_manager/env/lib/python3.8/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 "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 599, in run_until_complete
self.run_forever()
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 554, in run_forever
raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
OS: MacOS 10.13.6 Python version: 3.8.1 ipython version: 7.11.1 ipdb version: 0.12.3
The same works fine with pdb
ipdb
is a lightweight wrapper around IPython debugger. Have you checked IPython issue tracker ?
I came across this problem multiple times in different projects. In many situations project authors recommends to avoid using ipdb
. Unfortunately we have not much choices around. I decided to find a solution this time and found it. It is definitely ugly, but it works for me pretty fine:
def threaded(func):
def wrapper(*args, **kwargs):
result = []
def target():
result.append(func(*args, **kwargs))
thread = threading.Thread(target=target)
thread.start()
thread.join()
return result[0]
return wrapper
from IPython.terminal.debugger import TerminalPdb
TerminalPdb.cmdloop = threaded(TerminalPdb.cmdloop)
Probably the problem can be fixed somewhere inside prompt-toolkit
. pdb
will not help with it - he knows exactly nothing about asyncio
.
@apatrushev Thanks for documenting your workaround.
I started to experience this issue when I upgraded my ipython from 7.9.0 to 7.12.0 (looks like the change occured in 7.10.0: https://github.com/ipython/ipython/commit/c2a5384f364ecc5dded4ad892f13527880ae8517) in which ipython started to support prompt_toolkit 3.0.
I opened an issue in prompt_toolkit (https://github.com/prompt-toolkit/python-prompt-toolkit/issues/1084) to see if they have any suggestions, but very possibly ipython (or even ipdb) needs to find a workaround, because it looks the prompt_toolkit app was not designed to be run from inside an event loop.
A temporary workaround which worked for me is to pin my prompt_toolkit to 2.0.10.
Fixed in ipython: https://github.com/ipython/ipython/pull/12141
@om-saran When new IPython is released, can you confirm and close ?
@brianmaissy I have doubts whether this was the right way to go. Turns out that this makes IPDB hang up if prompt_toolkit
was talking to a "bad file descriptior", even with sset_trace()
. Use entr
to see what I'm talking about:
find . -iname '*.py' | entr -c python -c "import ipdb; ipdb.sset_trace()"
Previously, this would show the following tb:
[...]
File "/home/mrmino/.virtualenvs/tmp-f4b5e3c63682160/lib/python3.8/site-packages/prompt_toolkit/application/application.py", line 696, in _run_async
with self.input.raw_mode(), self.input.attach(
File "/home/mrmino/.virtualenvs/tmp-f4b5e3c63682160/lib/python3.8/site-packages/prompt_toolkit/input/vt100.py", line 257, in __enter__
os.write(self.fileno, b"\x1b[?1l")
OSError: [Errno 9] Bad file descriptor
Now it just hangs without responding to SIGTERM
, which leads me to believe that it also hangs up destructively when used e.g. by mistake in CICD test runs.
@MrMino I'm not familiar with the code enough to feel confident trying to address this.
Maybe the best way to address it is to open a separate issue?
Also maybe @jonathanslenders has some insight?
@brianmaissy I tried to fix this in different ways for the last few hours and I now believe that it comes down to a fundamental limitation with asyncio: switching / recursive event loops are not possible.
In this context having a debugger run an event loop in a separate thread seems like the most sane thing to do. We'd just need to check if the file descriptor for stdout is something usable.
I've been struggling with this one for a couple of days. I've tried a bunch of (ipdb, ipython, prompt-toolkit) version combinations and the one that's working the best for me is:
ipdb==0.10.0 ipython==4.2.1 prompt-toolkit==2.0.10
This only gives me
DeprecationWarning: inspect.getargspec() is
deprecated since Python 3.0, use inspect.signature() or inspect.getfullargspec()
as opposed to one of the following
Exception '0 (FD 0) is already registered'
Exception: Event loop is already running
your terminal doesn't support cursor position requests (CPR)
RuntimeError: Task <Task pending name='Task-6' coro=<Application.run_asy
nc() running at /usr/local/lib/python3.9/dist-packages/prompt_toolkit/ap
plication/application.py:856> cb=[_run_until_complete_cb() at /usr/lib/p
ython3.9/asyncio/base_events.py:184]> got Future <Task pending name='Tas
k-53' coro=<KeyProcessor._start_timeout.<locals>.wait() running at /usr/
local/lib/python3.9/dist-packages/prompt_toolkit/key_binding/key_process
or.py:397> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at
0x7fd30c5f7e50>()]>> attached to a different loop
When I call "self" for example when debugging with ipdb, I'll get a different result on the next call, and the first one again on the third call, I guess because of the asynchronous nature of the application/framework (Odoo) I'm developing in. IIRC this did not happen with ipython versions >= 5.