Skip to content

How to configure VS Code for a uv project

These steps assume a Python project managed with uv. If you haven’t created a project yet, see the project creation tutorial.

VS Code needs three things to work well with a uv project: the right Python interpreterThe program that reads and executes Python code. When you run "python3 hello.py", python3 is the interpreter. , a formatter and linter, and a test runner.

Install the required extensions

Install these extensions from the VS Code marketplace:

  • Python (ms-python.python)
  • Ruff (charliermarsh.ruff)

The Python extension handles interpreter selection and test discovery. It also installs Pylance (the language server behind IntelliSense and the python.analysis.* settings) and the Python Debugger extension (debugpy) automatically. The Ruff extension provides linting and formatting.

Select the Python interpreter

uv creates a virtual environmentAn isolated folder where Python installs packages for one project, keeping them separate from other projects and your system Python. Learn more → in .venv/ at the root of your project. The Python extension detects .venv directories automatically, so in most cases the interpreter is selected without any manual configuration.

Run uv sync to ensure the virtual environment exists. VS Code should pick up the .venv interpreter within a few seconds. If it doesn’t, open the Command Palette (Ctrl+Shift+P / Cmd+Shift+P) and choose Python: Select Interpreter, then pick the interpreter inside .venv.

Tip

If VS Code shows import errors or unresolved modules after adding a dependency with uv add, run uv sync and then reload the VS Code window (Ctrl+Shift+PDeveloper: Reload Window). The Python language server needs to re-index the environment.

Configure Ruff for formatting and linting

The Ruff extension replaces both a formatter (like Black) and a linter (like flake8). First add Ruff to the project so the editor and the command line run the same version:

uv add --dev ruff

The extension’s default importStrategy: "fromEnvironment" checks the active interpreter’s environment for a Ruff executable before falling back to its bundled copy. With Ruff installed by uv, the extension uses .venv/bin/ruff (.venv\Scripts\ruff.exe on Windows), so the editor and terminal never drift to different versions.

Then add these settings to .vscode/settings.json:

{
    "[python]": {
        "editor.defaultFormatter": "charliermarsh.ruff",
        "editor.formatOnSave": true,
        "editor.codeActionsOnSave": {
            "source.fixAll.ruff": "explicit",
            "source.organizeImports.ruff": "explicit"
        }
    }
}

The .ruff suffix on source.fixAll and source.organizeImports scopes each save action to this extension, so neither one triggers another formatter you have installed. The extension reads your project’s pyproject.toml, ruff.toml, or .ruff.toml automatically, so linting rules stay consistent between the editor and the command line.

Note

If you have the older Black Formatter or isort extensions installed, disable them. The Ruff extension handles both tasks.

Set up pytest

The Python extension can discover and run pytest tests from the VS Code sidebar. Add pytest to the project first so the extension finds it in the .venv:

uv add --dev pytest

Then add these settings:

{
    "python.testing.pytestEnabled": true,
    "python.testing.unittestEnabled": false,
    "python.testing.pytestArgs": ["tests"]
}

Save the file, open the Testing sidebar (beaker icon), and click Refresh Tests. Your test files appear as a tree, each test with a run icon beside it.

Combine the settings in one file

Here is a .vscode/settings.json that combines the Ruff and pytest configurations:

{
    "python.testing.pytestEnabled": true,
    "python.testing.unittestEnabled": false,
    "python.testing.pytestArgs": ["tests"],
    "[python]": {
        "editor.defaultFormatter": "charliermarsh.ruff",
        "editor.formatOnSave": true,
        "editor.codeActionsOnSave": {
            "source.fixAll.ruff": "explicit",
            "source.organizeImports.ruff": "explicit"
        }
    }
}

Commit this file to version control so everyone on the team gets the same formatter and test settings. The interpreter path stays out of it because VS Code auto-detects the .venv directory that uv creates.

Run a file with the play button

The play button (Run Python File) runs your file with the selected interpreter directly. It does not wrap the command in uv run, so it bypasses uv’s dependency resolution for inline script metadata. Run standalone scripts that declare their own dependencies from the integrated terminal with uv run script.py instead. For project code already installed in .venv by uv sync, the play button works fine.

Run Jupyter notebooks

To use Jupyter notebooks in VS Code with a uv project, add ipykernel as a development dependencyA package needed during development (testing, linting, formatting) that is not shipped to users of your project. Installed with the --dev flag. :

uv add --dev ipykernel

Open a .ipynb file and VS Code will prompt you to select a kernel. Choose the Python interpreter from your .venv directory. If no kernel appears, run uv sync first to ensure ipykernel is installed in the environment.

For more details, see How to run a Jupyter notebook with uv.

Add type checking with ty

ty is Astral’s type checker for Python, built by the same team behind uv and Ruff. The ty VS Code extension (astral-sh.ty) provides type error diagnostics directly in the editor.

Add ty to the project so the editor and the command line agree:

uv add --dev ty

Like Ruff, the extension defaults to ty.importStrategy: "fromEnvironment", so it uses the ty in .venv and falls back to its bundled copy only when the environment has none.

Note

ty is in beta. Expect rough edges compared to mature type checkers like mypy or pyright.

Debug with the Python Debugger

With the Python Debugger extension installed, open a Python file and run Python Debugger: Debug Python File from the Command Palette (Cmd+Shift+P / Ctrl+Shift+P). For more control, create a .vscode/launch.json:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Current File",
            "type": "debugpy",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal"
        },
        {
            "name": "Python: Module",
            "type": "debugpy",
            "request": "launch",
            "module": "your_package",
            "console": "integratedTerminal"
        }
    ]
}

Replace your_package with your project’s package name. The debugger uses the interpreter from .venv, so it has access to all dependencies installed by uv.

Tip

To debug a single pytest test, right-click the gutter icon next to the test in the editor and choose Debug Test. You can also use the bug icon in the Test Explorer sidebar.

Pick your next step

Fix common problems

  • “Import could not be resolved” for a third-party dependency. Run uv sync to install all dependencies, then reload the VS Code window. If the missing import is your own package, skip to Unresolved imports for your own package or a workspace member.

  • Wrong Python version. Check that uv python pin matches what VS Code shows in the status bar. If they differ, re-select the interpreter from the Command Palette.

  • Ruff settings not applied. The Ruff extension reads from pyproject.toml or ruff.toml in your project root. Run uv run ruff check . in the terminal to verify the rules match what the extension reports. To confirm the extension loaded the project’s Ruff rather than its bundled copy, run Ruff: Show server logs from the Command Palette; the startup lines print the path to the executable, which should sit inside .venv.

  • Unresolved imports for your own package or a workspace member. uv installs your project and its workspace members as editable installs, which leave a .pth file in .venv\Lib\site-packages (.venv/lib/python3.x/site-packages on macOS and Linux). Open that file and read the first line.

    A filesystem path resolves fine, so the fault is elsewhere: run uv sync and reload the window. A line starting with import means the build backendThe tool that does the actual work of turning your source code into an installable package. Examples include hatchling, setuptools, and uv_build. installed an import hook, the redirection PEP 660 permits. The pyright engine behind Pylance skips .pth lines that begin with import, so static analysis stops there.

    uv’s own build backend writes a plain path, so most projects never hit this. When yours does, reinstall the package with --config-settings editable_mode=compat if it builds with setuptools, which is the only backend that reads that setting; hatchling accepts the flag and ignores it. On any other backend, list the source directory in python.analysis.extraPaths. Leave python.analysis.enableEditableInstalls alone: Pylance already defaults it to true, its machine scope means VS Code ignores it in .vscode/settings.json, and Pylance describes it as experimental and for internal use.

Last updated on