python - How to trace the caller of an async function in VS Code Debug mode when the call stack is lost? - Stack Overflow

admin2025-04-17  2

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.

Share Improve this question edited Feb 1 at 3:44 Gino Mempin 29.8k31 gold badges120 silver badges167 bronze badges asked Jan 30 at 23:28 maplemaplemaplemaple 1,7631 gold badge12 silver badges36 bronze badges 6
  • When an async function is running, the original caller has already returned. – Barmar Commented Jan 30 at 23:36
  • @Barmar I understand. However, I'm diving into the source code of a large project and want to use debugging to analyze the data flow and function call relationships. Specifically, I need to determine which functions invoke the current function. Is there a way to make all async functions block execution? Thanks – maplemaple Commented Jan 30 at 23:44
  • Not AFAIK. Think about how most async functions work. 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
  • @Barmar I see, is there a way I can print who calls current async function? – maplemaple Commented Jan 31 at 3:53
  • It's called by the event loop when the asynchronous condition or event occurs. I don't think any information about the function that added the async callback to the event loop is saved anywhere. – Barmar Commented Jan 31 at 16:03
 |  Show 1 more comment

1 Answer 1

Reset to default 1

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.

转载请注明原文地址:http://anycun.com/QandA/1744886072a89015.html