How do I correctly set up the $PYTHONPATH
variable for my workspace in VisualStudio Code?
Background Information
I have installed two versions of GNURadio:
GNURadio version 3.7.11 installed by the Linux Mint package manager in /usr/lib/python2.7/dist-packages/gnuradio
GNURadio version 3.7.13.4 installed by PyBOMBS in /home/tejul/Documents/gr13/default/lib/python2.7/dist-packages/gnuradio
(my prefix directory is ~/Documents/gr13/default
)
I can use the newer version of GNURadio version only after I run the setup_env.sh
script (which -- among other things -- adds /home/tejul/Documents/gr13/default/lib/python2.7/dist-packages
to $PYTHONPATH
) and then start python in the terminal
tejul@Wacom:~/Documents/gr13/default$ ls
bin etc include lib libexec setup_env.sh share src
tejul@Wacom:~/Documents/gr13/default$ source ./setup_env.sh
tejul@Wacom:~/Documents/gr13/default$ python
Python 2.7.15rc1 (default, Nov 12 2018, 14:31:15)
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from gnuradio import gr
>>> gr.version()
'3.7.13.4'
>>>
Without modifying the $PYTHONPATH python -- naturally -- imports the older version of GNURadio.
I want to write, run, and debug python scripts for the new version of GNURadio in the VisualStudio Code. I've been trying to understand the selection of python interpreters, workspaces, and environments for VSCode.
As far as I understand it, the VSCode workspace setting python.pythonPath
is not to be confused with the environment variable $PYTHONPATH
. python.pythonPath
is the path to the python interpreter used for debugging or running the code, while $PYTHONPATH
is the environment variable which python uses to search for modules.
It looks like PyBOMBS did not install its own python interpreter into my prefix directory. So I need to use VSCode with my normal python interpreter located in /usr/bin/python2.7
. So redefining VSCode's python.pythonPath
or selecting another python interpreter would not help me.
I need to let VSCode use my own version of the environment variable $PYTHONPATH
which would tell my regular python interpreter to import modules preferably from /home/tejul/Documents/gr13/default/lib/python2.7/dist-packages
.
Problem
Following the documentation, I have created my own .env
file in the workspace directory which sets the order of preference for locations from which python should import the modules. Alas, it has no effect on the python interpreter.
Can you see anything that I am doing wrong here? I have also tried:
/home/tejul/Documents/gr13/default/lib/python2.7
, this did not help$PYTHONPATH
instead of PYTHONPATH
, this did not help.env
file, this did not helpPYTHONPATH="/home/tejul/Documents/gr13/default/lib/python2.7:/usr/lib/python2.7"
, this did not helpI have a situation that I believe is relatively common. I want a script to import a module from another directory. My python project is laid out as follows:
~/project/
|
|---modules/
|
|---mod.py
|---scripts/
|---script.py
in script.py
, I have from modules import mod
. So my PYTHONPATH
needs to be set to ~/project/
(something that PyCharm does automatically).
VSCode is a great editor, but everywhere else, it falls short, in my opinion. This is a perfect example of that.
I create a default launch.json
file to "run the current file". A "cwd": "${fileDirname}"
line has to be added to make things work like they do in PyCharm (FYI, a list of the built-in variables can be found here).
For debugging (the "play" button on the sidebar, or the F5 key), the PYTHONPATH
set in launch.json
or your .env
file takes effect. Note that in the .env
file, you cannot use variables such as ${workspaceRoot}
, but you can easily append or insert to the path by using the proper separator for your platform (;
for Windows and :
for everyone else).
Because I want to take advantage of that variable, I put this in my launch.json
:
"env": {"PYTHONPATH": "${workspaceFolder}:${env:PYTHONPATH}"}
It appears that you can prepend/append to whatever is inherited from the environment (this is not true for settings.json
; see below).
This will also work for the hotkey Ctrl+F5 (run without debugging).
For reference, here's the full file, which replicates what PyCharm does automatically:
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"cwd": "${fileDirname}",
"env": {"PYTHONPATH": "${workspaceFolder}:${env:PYTHONPATH}"}
}
]
}
If I hit the "play" button that appears on the top right of the editor window (when a python file is the active tab), it will not work. This runs the current file in a terminal, which doesn't pay attention to launch.json
at all. To make that work, you have to define PYTHONPATH
in a settings.json
file, by adding this:
"terminal.integrated.env.osx": {"PYTHONPATH": "${workspaceFolder}"}
(Note there are different values for each platform.) If you've selected a python interpreter (e.g. from a virtual environment), you will already have a settings.json
file in the .vscode
directory. Mine looks like this:
{
"python.pythonPath": "/Users/me/project/venv/bin/python3",
"terminal.integrated.env.osx": {"PYTHONPATH": "${workspaceFolder}"}
}
You can't append or insert values into the inherited PYTHONPATH
via the settings.json
file. It will only take one string, and it will not parse separators. So even though you could get the value using ${env:PYTHONPATH}
, you won't be able to do anything with it.
Moreover, you can't set the current working directory. Even though it would seem you could set it with "terminal.integrated.cwd": "${workspaceFolder}"
, it doesn't work. So if any of your scripts do anything with paths relative to their location in the tree, they won't work. The working directory will be your project root.
Note that any changes to the settings.json
file will require that you exit the integrated terminal and restart it.
Nothing I do to launch.json
regarding PYTHONPATH
makes any difference to pylint
, which will red-underline from modules import mod
, despite the fact I can put the cursor on mod
, hit F12, and the file opens. Snooping around linting settings, the defaults for mypy
include --ignore-missing-imports
. To replicate this behavior with pylint, add this to your settings.json
:
"python.linting.pylintArgs": [
"--disable=F0401"
]
Shame that we just have to work around this, but the autocomplete helps a lot when writing the import statements to begin with.
There are many layers to VSCode and it's hard to get things to work together. It seems multiple environments are floating around. In the end:
PYTHONPATH
for pylint
as that runs in some environment different than the integrated terminal and whatever is controlled by launch.json
, so I can only tell pylint
to ignore import errors.PYTHONPATH
either via an .env
file or in launch.json