I am debugging a Python async
function in VS Code, and I have hit a breakpoint inside the function. However, the call stack in the debug panel does not show the parent function that called it. Since without await
it clears the synchronous call stack, I cannot see who originally invoked the function.
I am debugging a Python async
function in VS Code, and I have hit a breakpoint inside the function. However, the call stack in the debug panel does not show the parent function that called it. Since without await
it clears the synchronous call stack, I cannot see who originally invoked the function.
I think I know what you're asking. Please ignore me if I'm wrong :-)
The following little script illustrates a problem:
import traceback
import asyncio
async def my_function():
await asyncio.sleep(1.0)
print("Debug me, stack trace follows")
traceback.print_stack()
async def main():
asyncio.create_task(my_function())
await asyncio.sleep(2.0)
print("Done")
asyncio.run(main())
The printout is as follows (Ubuntu, python3.13):
Debug me, stack trace follows
File "/home/paul/pyproj311/so/a25.py", line 14, in <module>
asyncio.run(main())
File "/usr/lib/python3.13/asyncio/runners.py", line 194, in run
return runner.run(main)
File "/usr/lib/python3.13/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
File "/usr/lib/python3.13/asyncio/base_events.py", line 708, in run_until_complete
self.run_forever()
File "/usr/lib/python3.13/asyncio/base_events.py", line 679, in run_forever
self._run_once()
File "/usr/lib/python3.13/asyncio/base_events.py", line 2027, in _run_once
handle._run()
File "/usr/lib/python3.13/asyncio/events.py", line 89, in _run
self._context.run(self._callback, *self._args)
File "/home/paul/pyproj311/so/a25.py", line 7, in my_function
traceback.print_stack()
Done
As you can see, the original call:
asyncio.create_task(my_function())
doesn't appear in the traceback. Asyncio has created a new Task, saved the coroutine object returned by my_function, and then started the Task from the event loop rather than from main(). The creation of the Task doesn't get onto the stack.
One workaround is to create a sync wrapper around the async function that you want to debug. The wrapper prints a stack trace first, and then returns a coroutine with the same functionality as the original async function. The following script resembles the first script, but captures the stack at the time of the original call.
import traceback
import asyncio
def my_function():
print("Debug me, stack trace follows")
traceback.print_stack()
return my_real_function()
async def my_real_function():
await asyncio.sleep(1.0)
async def main():
asyncio.create_task(my_function())
await asyncio.sleep(2.0)
print("Done")
asyncio.run(main())
The printout is now:
Debug me, stack trace follows
File "/home/paul/pyproj311/so/a25a.py", line 17, in <module>
asyncio.run(main())
File "/usr/lib/python3.13/asyncio/runners.py", line 194, in run
return runner.run(main)
File "/usr/lib/python3.13/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
File "/usr/lib/python3.13/asyncio/base_events.py", line 708, in run_until_complete
self.run_forever()
File "/usr/lib/python3.13/asyncio/base_events.py", line 679, in run_forever
self._run_once()
File "/usr/lib/python3.13/asyncio/base_events.py", line 2027, in _run_once
handle._run()
File "/usr/lib/python3.13/asyncio/events.py", line 89, in _run
self._context.run(self._callback, *self._args)
File "/home/paul/pyproj311/so/a25a.py", line 13, in main
asyncio.create_task(my_function())
File "/home/paul/pyproj311/so/a25a.py", line 6, in my_function
traceback.print_stack()
Done
The next-to-last line of the output now shows you the state of the stack when the function was called. Otherwise the two scripts are functionally identical, and it's not hard to figure out how to convert the first version into the second.
Sorry I don't use PyCharm - I can't answer how you can make this work inside the PyCharm debugger.
setTimeout()
doesn't run the callback until several seconds or minutes later -- you can't make this block everything else.addEventListener()
doesn't run the callback until you do something like click or type -- blocking makes even less sense then. – Barmar Commented Jan 31 at 1:23