How PDM Uses PEP 582 and Virtual Environments

PDM (Python Development Master) is a modern package and dependency manager that offers flexible dependency isolation by supporting both traditional virtual environments and the local __pypackages__ directory defined by PEP 582. While traditional tools mandate an active virtual environment, PDM allows developers to choose between creating an isolated .venv directory or installing packages directly inside the project root via PEP 582 mechanics. This guide explains how PDM implements both approaches, how it manages package resolution, and how to switch between them based on your project's workflow requirements.

Virtual Environments in PDM

In modern releases, PDM uses virtual environments as its default isolation strategy. This choice ensures out-of-the-box compatibility with the broader Python ecosystem, including IDEs, linters, and CI/CD pipelines.

When you initialize a project with pdm init, PDM prompts you to select a Python interpreter. By default, it automatically creates a dedicated virtual environment inside a .venv folder in your project root.

Key mechanics of PDM’s virtual environment implementation include:

PEP 582 Support in PDM

PEP 582 introduced the concept of Python local package directories, aiming to provide Python with an experience similar to Node.js's node_modules. Instead of creating a complete virtual environment with separate Python binaries, dependencies are installed directly into a folder named __pypackages__ located in the project root.

Although the Python Steering Council officially rejected PEP 582, PDM still maintains full support for developers who prefer this lightweight, activation-free workflow.

How PDM Implements PEP 582

When configured to use PEP 582, PDM adjusts how it installs and resolves libraries:

  1. Directory Structure: Dependencies are organized by Python version inside the project:
    __pypackages__/
    └── 3.11/
        ├── bin/
        ├── include/
        └── lib/
  2. Interpreter Resolution: Since standard Python interpreters do not natively read __pypackages__ without modifications, PDM intercepts the execution path. When using pdm run, PDM injects the path to __pypackages__/<version>/lib into the PYTHONPATH environment variable.
  3. Global Script Hook: PDM provides a setup command (pdm --pep582) that installs a custom sitecustomize.py file into your global Python installation. This hook enables standard commands like python script.py to recognize __pypackages__ without needing the pdm run prefix.

Configuring PDM: Virtual Environments vs. PEP 582

PDM lets you switch between virtual environments and PEP 582 at the global or project level.

Enabling Virtual Environments (Default)

To ensure PDM uses virtual environments for a project, configure the python.use_venv setting:

pdm config python.use_venv true

When this setting is enabled, commands like pdm install and pdm add place packages inside .venv.

Enabling PEP 582

To switch to the PEP 582 directory structure, disable the virtual environment setting:

pdm config python.use_venv false

Once disabled, remove any existing .venv directory and run pdm install. PDM will automatically construct the __pypackages__ hierarchy and install all dependencies there.

Choosing Between the Two Approaches