pythonpdb

Python Debugger (PDB): open currently active python file in editor


Is there some way to get during debugging with pdb:

and open the file in default text editor?

Example. After running the code below and then using s to step into Path execution and using some command/code I'm looking for I wanted to open file c:\python311\lib\pathlib.py at line 868 and at 5th symbol.

from pathlib import Path
import pdb; pdb.set_trace()
p = Path()

The problem is only about getting current python file in pdb context. Opening file itself is trivial os.system(r"C:\test.py"). Active line code and indent is needed so I'd be able to open it in VS Code at exact position: vsc --goto "C:\test.py:1:5" (comes from vsc --goto "<filepath>:<linenumber>:<x-coordinates>").

It would be nice if it was some native way to do it directly from pdb but since there's nothing related in the documentation, the solution is probably to make some custom method - which is okay too.

I've already tried to use inspect module but it seems that pdb is starting new frame where actual debugged python file is not accessible:

from inspect import currentframe, getframeinfo
frameinfo = getframeinfo(currentframe())
print(frameinfo.filename, frameinfo.lineno)
# <stdin> 1

Solution

  • I've found a solution. Investigating pdb module, I've found that Pdb class instance has self.curframe and self.botframe attributes allowing to access code frames and their paths. When we do import pdb; pdb.set_trace() it automatically creates an instance of Pdb class, we need to access.

    But I haven't found any other way to get the instance besides the hacky one - pdb.set_trace() is setting system trace (using sys.settrace) to Pdb_instance.trace_dispatch. Therefore we can retrieve Pdb_instance.trace_dispatch using sys.gettrace() and get Pdb instance with __self__.

    Final solution is the vscode() method in the snippet below. To make it always accessible with pdb you can put it in your pdb.py file (typically it's in Python311\Lib\pdb.py). I guess, the good place for it would be after def set_trace(*, header=None):.

    Then you'd be able to call import pdb; pdb.vscode() during pdb debugging and it will automatically open current file in VS Code. Voila!

    You can also add monkey_patch_vscode to your pdb - it will patch one of the Pdb methods and you'd be able to open VS Code with just vscode().

    PS Need to make sure you have vsc (link to VS Code) is accessable from your system Path.

    def monkey_patch_vscode():
        def wrap():
            original_setup = Pdb.setup
            def wrapped_setup(self, *args):
                res = original_setup(self, *args)
                self.curframe.f_globals["vscode"] = vscode
                return res
            return wrapped_setup
        Pdb.setup = wrap()
    monkey_patch_vscode()
    
    def vscode():
        """open file currently debugged with pdb in VS code
        (at current line and indent)"""
        import os, linecache, sys, types
        def get_current_pdb_file():
            pdb = sys.gettrace().__self__
            # there is also .botframe - the frame when pdb entered
            frame = pdb.curframe # type: types.FrameType
            return frame.f_code.co_filename, frame.f_lineno
        filepath, line = get_current_pdb_file()
        current_line = linecache.getlines(filepath)[line]
        indent = len(current_line) - len(current_line.lstrip()) + 1
        def vscode_run(filepath, line, indent=1):
            # `line` and `indent` starting from 1
            os.system(f'vsc --goto "{filepath}:{line}:{indent}"')
    
        vscode_run(filepath, line, indent)