inducer / pudb

Full-screen console debugger for Python
https://documen.tician.de/pudb/
Other
3k stars 230 forks source link

Can I get the interrupt handler to work with async code? #437

Closed admirabilis closed 3 years ago

admirabilis commented 3 years ago

While this works as expected, if I press Ctrl+C:

import time
import pudb; pudb.set_trace(paused=False)

def main():
    time.sleep(10)

main()

pudb won't show anything inside main() here:

import asyncio
import pudb; pudb.set_trace(paused=False)

async def main():
    await asyncio.sleep(10)

asyncio.run(main())

The documentation says the interrupt handler won't work with threading, but it doesn't talk about async code.

asmeurer commented 3 years ago

Maybe it can be made to work. I don't know enough about async. Then again, based on https://vorpus.org/blog/control-c-handling-in-python-and-trio/ I also wouldn't be surprised if it can't work with asyncio (but maybe with trio?).

admirabilis commented 3 years ago

I have tried asyncio and uvloop, but not trio yet, thanks for the suggestion!

admirabilis commented 3 years ago

Makes no difference, unfortunately. SIGINT is properly caught while using any of them, but the debugger won't step into main().

inducer commented 3 years ago

How does pdb do in this situation?

admirabilis commented 3 years ago

Same thing. I used boltons to handle the calling of pdb on SIGINT (and had previously written my own handler, before trying pudb):

import asyncio
from boltons.debugutils import pdb_on_signal; pdb_on_signal()

async def main():
    await asyncio.sleep(10)

asyncio.run(main())
inducer commented 3 years ago

I think what you're seeing here has more to do with how signal handling in Python works than anything else. The actual SIGINT handler sets a flag to tell the interpreter loop to break, but it doesn't do much else (it can't, at least not safely, without acquiring the GIL, which it is unlikely to be able to do). For the duration of the sleep, however, the code hangs in select(), however select() returns with EINTR whenever a signal is caught. The question is whether the code managing the select() stops to check if any signal handling is to be done.

So, in summary, IMO this behavior has little to do with pudb (as evidenced by the fact that pdb behaves the same way) and more with how select() is called in your particular circumstance.

FWIW, if you put a from pudb import set_trace; set_trace() inside of main, pudb can debug async code just fine.

admirabilis commented 3 years ago

FWIW, if you put a from pudb import set_trace; set_trace() inside of main, pudb can debug async code just fine.

It works without paused=False indeed. Otherwise the result is the same. I had already tried that before opening this bug report. But the idea is that I have a code too big that was sometimes hanging, and I still couldn't figure out which loop is stuck.