Back to Scaling Python Applications guides

Introduction to PDM: A Python Project and Dependency Manager

Stanley Ulili
Updated on March 12, 2025

PDM 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.

Prerequisites

To follow along with this tutorial, you'll need a recent version of Python 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):

 
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):

.bashrc
export PATH="$HOME/.local/bin:$PATH"  

Then, apply the changes:

 
source ~/.bashrc

To verify that PDM has been installed successfully, run:

 
pdm --version
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:

 
mkdir pdm-demo && cd pdm-demo

Initialize a new PDM project with:

 
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:

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:

 
pdm add requests
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:

 
pdm add numpy pandas matplotlib

Or specify version constraints:

 
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:

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:

 
pdm run python app.py
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:

 
pdm add fastapi

This command installs FastAPI and updates your project configuration:

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:

 
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:

 
pdm update

Or update specific packages:

 
pdm update fastapi pydantic

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

 
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:

 
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:

 
pdm run python -c "import requests; print(requests.__version__)"
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:

 
pdm add sqlalchemy

This results in:

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:

 
pdm add --dev pytest mypy black

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

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:

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

This results in:

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:

 
pdm add -G web flask jinja2

This adds dependencies to the optional dependencies section:

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:

 
pdm run pytest
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:

 
pdm install --prod

Install production dependencies along with a specific optional group:

 
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:

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:

 
pdm info
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:

 
pdm use python3.12
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.

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

Now switch the Python version like this:

 
pdm use python3.12
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:

 
pdm python install --list
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:

 
pdm python install

Or to install the minimum compatible version:

 
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.

Author's avatar
Article by
Stanley Ulili
Stanley Ulili is a technical educator at Better Stack based in Malawi. He specializes in backend development and has freelanced for platforms like DigitalOcean, LogRocket, and AppSignal. Stanley is passionate about making complex topics accessible to developers.
Got an article suggestion? Let us know
Next article
Getting Started with SQLAlchemy ORM for Python
Learn SQLAlchemy with this step-by-step tutorial! Discover how to set up a SQLAlchemy project with SQLite, define database models, and perform CRUD operations (Create, Read, Update, Delete) using Python. This guide covers everything from database connections to data manipulation, making it perfect for beginners and those looking to enhance their SQLAlchemy skills.
Licensed under CC-BY-NC-SA

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Make your mark

Join the writer's program

Are you a developer and love writing and sharing your knowledge with the world? Join our guest writing program and get paid for writing amazing technical guides. We'll get them to the right readers that will appreciate them.

Write for us
Writer of the month
Marin Bezhanov
Marin is a software engineer and architect with a broad range of experience working...
Build on top of Better Stack

Write a script, app or project on top of Better Stack and share it with the world. Make a public repository and share it with us at our email.

community@betterstack.com

or submit a pull request and help us build better products for everyone.

See the full list of amazing projects on github