# VS Code: Run currently opened Python script as a module

TLDR;

I'm lazy and want to start all open Python scripts by hitting F5 and expect absolute and relative imports to work fine, no matter where the script resides within the folder structure.

Skip to the end to access a ready-made `launch.json` configuration.

# Setup
Imagine your VS Code workspace looks as follows:

```
.
│   foo.py
│   bar.py
│
└───folder
│   │   baz.py
│   
└───utils
    │   handy_module.py
``` 

Additionally, `bar.py` as well as `baz.py` both reference `handy_module.py` with an import such as `import utils.handy_module`.

# Running scripts directly

Running `foo.py` as well as `bar.py` directly as a script works just fine, but running `baz.py` using the command line `python folder/baz.py` ends up with the error "ModuleNotFoundError: No module named 'utils'". I'm no expert in the exact mechanics of how packages work (see [this SO article](https://stackoverflow.com/questions/21233229/whats-the-purpose-of-the-package-attribute-in-python/48833828#48833828) as well as [this RealPython article](https://realpython.com/absolute-vs-relative-python-imports/) for a start), but the gist is that we would need to run `baz.py` with the `-m` switch, such as `python -m folder.baz`.

# Running specific scripts using `-m`

Although VS Code gives you the option to run a script as a module, using the `-m` option, the `launch.json` configuration generally expects a specific module name.

One entry would usually look like this:

```json
{
    "name": "Python: Launch baz.py as module",
    "type": "python",
    "request": "launch",
    "module": "folder.baz",
    "justMyCode": true
}
``` 
This is fine as long as you don't want to run a lot of different files. Then the debug configuration menu gets clogged very quickly, and as a lazy user, I just want to be able to hit F5 with any open script and expect it to work. Also, referring to the scripts explicitly makes it harder to rename them later, as I would have to edit `launch.json` each time. So this is no bueno for larger projects.

# Running open scripts of root folder using `-m`

If the scripts that you want to run reside in the root folder of the current workspace (as is the case for `foo.py` and `bar.py`), then you could make them run dynamically by replacing `foo` with `${fileBasenameNoExtension}` as per [VS Code's variables documentation](https://code.visualstudio.com/docs/editor/variables-reference).

```json
{
    "name": "Python: Launch open file as module (works in root folder)",
    "type": "python",
    "request": "launch",
    "module": "${fileBasenameNoExtension}",
    "justMyCode": true
}
```

But, if your scripts reside in a subfolder of your workspace (as is the case for `folder/baz.py`), then the built-in variables of VS Code don't allow you to build a syntactically correct module path, effectively replacing all path separators with dots.

# Running any open script using `-m`

You can overcome this by using the enhanced variables of the VS Code extension "Command Variable". Installing it from the extensions marketplace gives you access to the variable `${command:extension.commandvariable.file.relativeDirDots}`. This finally gives you the following `launch.json` configuration:
```
{
    // This one needs the VS Code Extension "Command Variable"
    "name": "Python: Launch as Module",
    "type": "python",
    "request": "launch",
    "module": "${command:extension.commandvariable.file.relativeDirDots}.${fileBasenameNoExtension}",
    "justMyCode": true
}
```

Hooray! I can now finally run all open scripts directly by hitting F5, and the absolute/relative imports work as well.
