# Introduction to PDM: A Python Project and Dependency Manager

[PDM](https://pdm.fming.dev/) is a modern Python package and dependency manager prioritizing compliance with PEP standards and developer experience. 

It aims to bring best practices from other language ecosystems to Python, offering a more intuitive and efficient way to manage projects and dependencies.


This article will introduce you to PDM, explain its core concepts and advantages, and guide you through implementing PDM best practices in your Python development workflow.

[ad-logs]

## Prerequisites

To follow along with this tutorial, you'll need a recent version of [Python](https://www.python.org/downloads/) installed on your system (Python 3.13 or newer is recommended). While PDM can help manage Python versions, having at least one Python installation will simplify getting started.

## Why use PDM?

Before diving into PDM, let's understand why it significantly improves over traditional Python project management approaches. Here's what makes PDM unique:

- PEP-621 compatible project management: PDM fully supports the official Python packaging standards, using `pyproject.toml` as the single source of truth for your project configuration.

- Virtual environment automation: PDM automatically creates and manages isolated environments for your projects without requiring explicit activation.

- Deterministic dependency resolution: PDM ensures reproducible builds through its advanced dependency resolver and lock file system.

- Powerful plugin system: PDM can be extended with plugins, allowing you to customize and enhance your workflow based on specific project needs.

A good way to understand PDM is to consider it a blend of `pip`, `virtualenv`, and `poetry`, combined into a unified package management and workflow tool that follows modern Python standards.

## Installing PDM

This section guides you through installing PDM on your machine.

The recommended way to install PDM on Linux is by using the official installation script(On macOS, you can easily install it with Homebrew):  

```command
curl -sSL https://pdm-project.org/install-pdm.py | python3 -
```

The installer will place PDM in your `$HOME/.local/bin`.

Make sure this location is in your PATH.  Add the following line to your shell configuration file (like `.bashrc`, `.zshrc`, or `.profile`):


```text
[label .bashrc]
export PATH="$HOME/.local/bin:$PATH"  
```

Then, apply the changes:

```command
source ~/.bashrc
```

To verify that PDM has been installed successfully, run:

```command
pdm --version
```

```text
[output]
PDM, version 2.22.4
```

This output confirms that PDM has been installed successfully and is ready for use.


## Getting started with PDM

Now that PDM is installed, let's explore its core functionality. PDM simplifies Python project management by automating virtual environments and dependency installation while strictly adhering to modern Python packaging standards.

To start a new project with PDM, create a project directory and navigate into it:

```command
mkdir pdm-demo && cd pdm-demo
```

Initialize a new PDM project with:

```command
pdm init
```

This command launches an interactive setup that prompts you to configure key project details, such as the Python interpreter, project name, version, license, author information, and dependencies:

```text
[output]

Creating a pyproject.toml for PDM...
Please enter the Python interpreter to use
 0. cpython@3.13 (/opt/homebrew/bin/python3)
 1. cpython@3.13 (/opt/homebrew/bin/python3.13)
 2. cpython@3.12 (/opt/homebrew/bin/python3.12)
 3. cpython@3.9 (/usr/bin/python3)
 4. cpython@3.13 (/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/bin/python3.13)
Please select (0): 1
Virtualenv is created successfully at /Users/stanley/pdm-demo/.venv
Project name (pdm-demo): 
Project version (0.1.0): 
Do you want to build this project for distribution(such as wheel)?
If yes, it will be installed by default when running `pdm install`. [y/n] (n):  
License(SPDX name) (MIT): 
Author name (Your name): 
Author email (youremail@gmail.com): 
Python requires('*' to allow any) (==3.13.*): 
Project is initialized successfully
```

Once completed, PDM generates a `pyproject.toml` file containing your project's metadata and configuration. 

This file is the central project descriptor, defining dependencies, package settings, and other relevant details.

Unlike traditional workflows that require manually creating and activating a virtual environment, PDM automates this process, streamlining project setup.

To install a package, simply run:

```command
pdm add requests
```

```text
[output]

Adding packages to default dependencies: requests
  0:00:03 🔒 Lock successful.  
Changes are written to pyproject.toml.
Synchronizing working set with resolved packages: 5 to add, 0 to update, 0 to remove

  ✔ Install idna 3.10 successful
  ✔ Install certifi 2025.1.31 successful
  ✔ Install requests 2.32.3 successful
  ✔ Install urllib3 2.3.0 successful
  ✔ Install charset-normalizer 3.4.1 successful

  0:00:01 🎉 All complete! 5/5
```

During installation, PDM automatically creates a virtual environment if one doesn’t exist, installs the package along with its dependencies, and updates both pyproject.toml and pdm.lock to track dependencies.

You can also install multiple packages at once:

```command
pdm add numpy pandas matplotlib
```

Or specify version constraints:

```command
pdm add "django>=4.2"
```

As mentioned earlier, one of PDM’s key features is that you don’t need to manually activate virtual environments. To test your installation, create a simple Python script:

```python
[label app.py]
import requests

response = requests.get("https://api.github.com/users/pdm-project")
data = response.json()

print(f"PDM GitHub repository: {data['html_url']}")
print(f"PDM has {data['public_repos']} public repositories")
```

Run it using PDM:

```command
pdm run python app.py
```

```text
[output]
PDM GitHub repository: https://github.com/pdm-project
PDM has 19 public repositories
```

This automatically uses the correct virtual environment, making it easy to work with multiple projects without environment conflicts.

You've now seen how PDM simplifies dependency management and project setup. It removes many of the manual steps required with traditional tools while ensuring that your project follows modern Python best practices.



## Managing dependencies with PDM
 
Now, let's dive deeper into PDM's powerful dependency management capabilities that make package installation, version control, and environment synchronization more reliable.

PDM's dependency management approach is built around two key files:

- `pyproject.toml`: Declares your project's dependencies with version specifications
- `pdm.lock`: Records the exact versions of all dependencies and sub-dependencies

When you add a package using PDM, it updates both files automatically:

```command
pdm add fastapi
```

This command installs FastAPI and updates your project configuration:

```toml
[label pyproject.toml]
[project]
name = "pdm-demo"
version = "0.1.0"
description = ""
authors = [
    {name = "Your Name", email = "your.email@example.com"},
]
dependencies = ["requests>=2.32.3", ..., "fastapi>=0.115.11"]
requires-python = "==3.13.*"
readme = "README.md"
license = {text = "MIT"}

[tool.pdm]
distribution = false
```

Meanwhile, PDM also creates a detailed lock file that captures the exact versions of FastAPI and all its sub-dependencies. This ensures that anyone else working on your project will get exactly the same environment.

PDM provides fine-grained control over version constraints. For example:

```command
pdm add "sqlalchemy>=2.0.0" "pydantic<2.0.0"
```

This adds SQLAlchemy with a minimum version of 2.0.0 and Pydantic with a maximum version below 2.0.0.

To update dependencies to their latest compatible versions:

```command
pdm update
```

Or update specific packages:

```command
pdm update fastapi pydantic
```

If you no longer need a package, remove it with:

```command
pdm remove fastapi
```

This not only uninstalls the package but also updates the `pyproject.toml` and `pdm.lock` files to keep your dependency declarations in sync with what's installed.

When collaborating with a team or setting up your project on a new machine, you don't need to install each package individually. Simply clone the repository with the `pyproject.toml` and `pdm.lock` files, then run:

```command
pdm install
```

This installs the exact versions specified in the lock file, ensuring a consistent environment across all development machines and deployment targets.

PDM also makes it easy to run commands within the project's environment without activation:

```command
pdm run python -c "import requests; print(requests.__version__)"
```

```text
[output]
2.32.3
```

Using PDM's modern approach to dependency management helps maintain consistency across development, testing, and production environments, reducing compatibility issues that often arise in Python projects.


## Working with dependency groups in PDM

Most Python projects require different sets of dependencies for various purposes. PDM provides a clean solution to this challenge through dependency groups, which allow you to categorize and manage dependencies based on their usage.

Dependency groups offer several advantages:

- Clear separation between runtime and development dependencies
- Ability to install only what's needed for specific tasks
- Improved project organization and documentation
- Reduced environment size in production deployments

As you have already seen, when you add a package with `pdm add`, it goes into the main dependencies section in `pyproject.toml`:

```command
pdm add sqlalchemy
```

This results in:

```toml
[label pyproject.toml]
[project]
dependencies = [
    "sqlalchemy>=2.0.39",
]
```

However, tools used only during development—like testing frameworks, type checkers, or linters—should be kept separate. PDM makes this easy with the `--dev` or `-d` flag:

```command
pdm add --dev pytest mypy black
```

This creates a new optional dependency group named "dev":

```toml
[label pyproject.toml]
...
[dependency-groups]
dev = [
    "pytest>=8.3.5",
    "mypy>=1.15.0",
    "black>=25.1.0",
]
```

Development dependencies won't be included in your package's distribution metadata, making them perfect for tools only needed during development.

You can create multiple development dependency groups:

```command
pdm add -dG test pytest pytest-cov
```
```command
pdm add -dG lint flake8 black
```

This results in:

```toml
[label pyproject.toml]
...
[dependency-groups]
...
test = [
    "pytest>=8.3.5",
    "pytest-cov>=6.0.0",
]
lint = [
    "flake8>=7.1.2",
    "black>=25.1.0",
]
```

For dependencies that should be included in your package metadata (like optional features), use the `-G/--group` option instead:

```command
pdm add -G web flask jinja2
```

This adds dependencies to the optional dependencies section:

```toml
[label pyproject.toml]
...
[project.optional-dependencies]
web = [
    "flask>=3.1.0",
    "jinja2>=3.1.6",
]
...
```

### Using dependencies from specific groups

To run commands using packages from specific groups, use `pdm run`. For example, to execute a package from development dependencies:


```command
pdm run pytest
```
```text
[output]
============================================================== test session starts ==============================================================
platform darwin -- Python 3.13.2, pytest-8.3.5, pluggy-1.5.0
rootdir: /Users/stanley/pdm-demo
configfile: pyproject.toml
plugins: cov-6.0.0
collected 0 items                                                                                                                               

============================================================= no tests ran in 0.01s =============================================================
```
 

To install dependencies selectively, use the appropriate `pdm install` command.  

Install only production dependencies: 

```command
pdm install --prod
```

Install production dependencies along with a specific optional group:  

```command
pdm install -G web
```

Different command combinations will install different sets of dependencies:

- `pdm install` installs production + all development dependencies
- `pdm install -G web` installs production + "web" optional group + all development dependencies
- `pdm install -G test` installs production + only the "test" development group
- `pdm install --prod` installs production dependencies only

Organizing dependencies into logical groups with PDM improves project maintainability by defining package usage and optimizing environments for specific tasks.

## Python version management with PDM

Managing Python versions across different projects is a common challenge for developers. PDM simplifies this by providing built-in Python version management capabilities that ensure your project always runs with the correct Python version.

PDM's approach to Python version management is based on two key concepts:

- Declaring Python version requirements in `pyproject.toml`
- Automatically selecting an appropriate Python interpreter based on those requirements

When you initialize a PDM project, you specify the Python version requirements:

```toml
[label pyproject.toml]
[project]
requires-python = "==3.13.*"
```

This example indicates that your project is compatible with any 3.13.x version. When creating a new project, PDM suggests a default value based on your selected Python version.

PDM will automatically select an appropriate Python interpreter from those available on your system. To see which Python version PDM is using for your project:

```command
pdm info
```

```text
[output]
PDM version:
  2.22.4
Python Interpreter:
  /Users/stanley/pdm-demo/.venv/bin/python (3.13)
Project Root:
  /Users/stanley/pdm-demo
Local Packages:
 ```

If you need to use a different Python version, ensure it's compatible with your project's `requires-python` specification.

For example, if you try to switch to Python 3.12 with a project that requires 3.13:

```command
pdm use python3.12
```

```text
[output]
[NoPythonVersion]: No Python interpreter matching requires-python="==3.13.*" is found.
```

To switch Python versions successfully, update the `requires-python` value in your `pyproject.toml` file to `>=3.12`.  

```toml
[label pyproject.toml]
[project]
name = "pdm-demo"
version = "0.1.0"
...
[highlight]
requires-python = ">=3.12"
[/highlight]
....
```  

Now switch the Python version like this:
 
```command
pdm use python3.12
```

```text
[output]
INFO: Using the first matched interpreter.
Virtualenv is created successfully at /Users/stanley/pdm-demo/.venv
Using Python interpreter: /Users/stanley/pdm-demo/.venv/bin/python 
(3.12)
```

This ensures that your project's dependencies remain compatible with the Python version you're using.

### Installing Python versions with PDM

A key feature of PDM is its ability to install different Python versions directly.

To see all available Python versions for installation:

```command
pdm python install --list
```
```text
[output]
cpython@3.13.2
cpython@3.13.1
cpython@3.13.0
cpython@3.12.9
cpython@3.12.8
cpython@3.12.7
...
cpython@3.11.11
cpython@3.11.10
cpython@3.10.4
cpython@3.10.3
cpython@3.8.13
cpython@3.8.12
pypy@3.11.11
pypy@3.10.19
pypy@3.10.14
...
pypy@3.9.15
pypy@3.8.16
pypy@3.8.15
```

PDM can also automatically choose the best Python version based on your project's `requires-python`. For example, to automatically install and use the highest compatible version:

```command
pdm python install
```

Or to install the minimum compatible version:

```command
pdm python install --min
```
These features make PDM a convenient tool for managing Python environments efficiently.


## Final thoughts
This article has explored PDM's key features to help modernize your Python development workflow. PDM combines modern dependency management, project structure, and Python version handling into a single, standards-compliant tool.

For more details, visit the [official PDM documentation](https://pdm.fming.dev/).