Easy way to launch VS Code Python debugger in any Python code

The Problem

This will probably not be a huge revelation to every one, since this info is available in public documentation. Yet for a long time, my impression of VS Code debugging interface was that it's a pain to configure. If you only need to debug a single script - sure, there's a button for that. If you need to debug a simple Django project - there is a preset for you. But what if you're working on several projects and each one starts and runs tests from a shell script or other task runner and you need to debug a test under specific task parameters? There's no way I'm gonna waste time making sure that VS Code passes the same command-line arguments to the debugger when I want to debug a unit test.

Yes, you can drop into pdb after running code in your usual way. But pdb has a terminal interface that requires training and I don't like it.

The Solution

Turns out there's a Microsoft package called debugpy that allows you to connect to VS Code debugger from anywhere in your code. The setup guide is posted on the VS Code website:

Debugging configurations for Python apps in Visual Studio Code
Details on configuring the Visual Studio Code debugger for different Python applications.

Everything you need should be available there, but I'll still leave here a quick recipe for my future self.

The Recipe

1. Make sure you have `debugpy` installed in your project's virtual environment:

pip install debugpy

2. Open your global VS Code settings:

Ctrl+Shift+P
Preferences: Open Settings (JSON)

3. Add the following launch config:

    "launch": {
        "version": "0.2.0",
        "configurations": [
            {
                "name": "Python: Attach",
                "type": "python",
                "request": "attach",
                "connect": {
                    "host": "localhost",
                    "port": 5678
                },
                "pathMappings": [
                    {
                        "localRoot": "~",
                        "remoteRoot": "~"
                    }
                ]
            }
        ]
    }

(You can put it into the workspace-scoped launch config instead of course. For that, search for the command "Open launch.json".)

4. Put this snippet in your code:

import debugpy

debugpy.listen(5678)
print("Waiting for debugger")
debugpy.wait_for_client()

5. Put a breakpoint where you need it through VS Code.

6. Run your code and after you see the message run the "Python: Attach" configuration in the debugger tab. There you go.

7. If you're using pytest make sure to pass the -s flag to it, otherwise, it will capture the printed message and you won't see it.