How fix Python import error in VS Code editor when using a Dev Container?

Question:

I’ve opened a project with the following structure in VS Code (1.71.0 on macOS, Intel) and activated a Dev Container (I’ve tried the default Python 3.9 and 3.10 containers from Microsoft, with and without using python3 -m venv ...):

project/
  .devcontainer/
    devcontainer.json
    Dockerfile
  foo/
    foo/
      tests/
        test_bar.py  <-- IDE reports import error in this file
      resources/
      __init__.py
      bar.py
    setup.py

In VS Code’s terminal window, I can successfully run test_bar.py from directory project/foo with:

python3 -m unittest discover foo/tests -p 'test_*.py'

So the project is valid and runs OK from the command line.

But when I open file project/foo/foo/tests/test_bar.py in VS Code, I see the error Unable to import 'foo' pylint(import-error) underlined in red for the following line:

from foo import bar

I see similar supposed errors for external packages I’ve installed with pip3 install.

I’ve tried to inform VS Code by adding various relative and absolute paths (e.g. /workspaces/project/foo) to various places in project/.devcontainer.json, such as at:

  • customizations.vscode.settings
    • python.analysis.extraPaths
    • python.autoComplete.extraPaths
    • python.testing.unittestargs

But I’ve had no luck so far (after many IDE restarts and container image rebuilds).

So I’m left wondering; how should one fix such IDE-flagged import errors in VS Code when using a Dev Container?

Additional Info

As a file was requested in the comments, here are the key test project files I used (I’ve not fixed any paths; my last test project was named vscode-python-dev-container, not project, which I used as shorthand above).

devcontainer.json (with the containerEnv section added for the suggested PYTHONPATH change):

// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/python-3
{
    "name": "Python 3",
    "build": {
        "dockerfile": "Dockerfile",
        "context": "..",
        "args": { 
            // Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6
            // Append -bullseye or -buster to pin to an OS version.
            // Use -bullseye variants on local on arm64/Apple Silicon.
            "VARIANT": "3.10-bullseye",
            // Options
            "NODE_VERSION": "lts/*"
        }
    },

    "containerEnv": {
        "PYTHONPATH": "/workspaces/vscode-python-dev-container/foo"
    },

    // Configure tool-specific properties.
    "customizations": {
        // Configure properties specific to VS Code.
        "vscode": {
            // Set *default* container specific settings.json values on container create.
            "settings": { 
                "python.defaultInterpreterPath": "/usr/local/bin/python",
                "python.linting.enabled": true,
                "python.linting.pylintEnabled": true,
                "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
                "python.formatting.blackPath": "/usr/local/py-utils/bin/black",
                "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
                "python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
                "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
                "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
                "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
                "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
                "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
            },
            
            // Add the IDs of extensions you want installed when the container is created.
            "extensions": [
                "ms-python.python",
                "ms-python.vscode-pylance"
            ]
        }
    },

    // Use 'forwardPorts' to make a list of ports inside the container available locally.
    // "forwardPorts": [],

    // Use 'postCreateCommand' to run commands after the container is created.
    // "postCreateCommand": "pip3 install --user -r requirements.txt",

    // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
    "remoteUser": "vscode"
}

Dockerfile:

# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/python-3/.devcontainer/base.Dockerfile

# [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster
ARG VARIANT="3.10-bullseye"
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}

# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10
ARG NODE_VERSION="none"
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi

# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
# COPY requirements.txt /tmp/pip-tmp/
# RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt 
#    && rm -rf /tmp/pip-tmp

# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive 
#     && apt-get -y install --no-install-recommends <your-package-list-here>

# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1

bar.py:

"""An example module."""
JSON_STR = '{"a": 1}'

test_bar.py:

"""An example test."""
import json
import unittest
import requests  # test external lib access
from foo import bar

class TestFooBar(unittest.TestCase):
    """An example test class."""
    a = requests.__name__  # to eliminate warning above that package isn't used
    def test_foo_bar_json(self):
        """An example test."""
        data = json.loads(bar.JSON_STR)
        self.assertEqual(data['a'], 1)
Asked By: David

||

Answers:

I often have this same issue with Python+Devcontainer. What works for me is:

  1. setting this env variable: export PYTHONPATH=/workspace, you can also try with /workspaces or /yourProjectName depending on your setup.

  2. restart the Python Language Server with Ctrl+Shift+P > Python: Restart Language Server.

Note that you do not need to rebuild the container. When you find the correct value for PYTHONPATH, you can add it to your Devcontainer configuration.

Answered By: Giubots

Is this issue completely resolved?
I believe it has to do something with your directory structure.

Please have a look at recommended directory structure here

.devcontainer/
  devcontainer.json
.devcontainer/
  Dockerfile
README.rst
LICENSE
setup.py
requirements.txt
foo/
  __init__.py
foo/
  bar.py
tests/
  test_bar.py
docs/
  conf.py    // optional
docs/
  index.rst  // optional

If you don’t want to disturb your directory structure, you may consider below two entries:

  1. In the devcontainer.json file, give context:"."
  2. In the Dockerfile, adding appropriate entry for WORKDIR
Answered By: Gsb
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.