
Everything You Need to Know About Python Project Management with PDM
Table of Contents
- Introduction
- Why Use a Project Manager?
- What is PDM?
- Creating and Managing a Python Project with PDM
- Conclusion
Introduction
Python project management is an essential practice for ensuring project productivity and maintainability. Whether working alone or in a team, a good management tool helps you organize dependencies, create reproducible environments, and simplify application lifecycle. Historically, Python developers used pip and virtualenv, but these tools show their limitations when faced with modern requirements. Today, several tools like Conda
, Hatch
, PDM
, Pipenv
, Poetry
, and UV
offer more comprehensive solutions. This article focuses on PDM
, a modern tool that respects Python standards.
Why Use a Project Manager?
Using a modern project manager like PDM solves several crucial Python development problems:
- Consistent Dependency Management
- Automatic version conflict resolution
- Precise version locking to ensure reproducibility
- Clear separation between production and development dependencies
- Environment Isolation
- Automatic virtual environment creation
- Prevents conflicts with system packages
- Ensures portability across different machines
- Project Standardization
- Consistent project structure
- Centralized configuration in pyproject.toml
- Respect for modern Python standards (PEP 517, PEP 518, PEP 621)
- Task Automation
- Integrated development scripts
- Standardized commands for build, testing, linting
- Hooks to automate recurring tasks
- Facilitated Collaboration
- Ensures all developers use the same versions
- Simplifies continuous integration
- Facilitates onboarding of new developers
What is PDM?
PDM is a modern package and dependency manager for Python, designed to simplify and optimize developer workflows. It supports the latest PEP standards, notably PEP 517 and PEP 621, and offers a flexible and powerful approach to Python project management.
Main PDM Characteristics:
-
Fast Dependency Resolution:
PDM
uses an efficient dependency resolver, particularly suitable for large binary distributions, ensuring quick and reliable package installation. -
PEP 517 Compliant Build Backend: It offers a build backend compatible with the PEP 517 specification, facilitating the creation and distribution of Python packages.
-
Project Metadata According to PEP 621:
PDM
uses the project metadata format defined by PEP 621, allowing clear and standardized configuration of Python projects. -
Flexible Plugin System: It has a powerful plugin system, allowing extension of functionalities according to specific project needs.
-
Versatile User Scripts:
PDM
allows the use of custom scripts, offering increased flexibility in managing development tasks. -
Integrated Python Installation: It includes the ability to install specific Python versions, simplifying development environment management.
-
Centralized Installation Cache:
PDM
offers a centralized cached installation, similar to tools likepnpm
, optimizing disk space usage and installation speed.
PDM
experimentally supports UV
as a resolver and installer , thus making the process faster and more performant.
Alternatives to PDM:
Choosing a project manager depends primarily on your specific needs and personal preferences. Each tool has its strengths and limitations, and it’s important to find the one that best integrates with your workflow. Although PDM is not the only available solution, it stands out for its simplicity and flexibility, making it an excellent option for many developers. That said, here’s an overview of popular PDM alternatives:
Strengths | Weaknesses | |
---|---|---|
Conda |
|
|
Hatch |
|
|
Pipenv |
|
|
Poetry |
|
|
UV |
|
|
Creating and Managing a Python Project with PDM
To start using PDM, you must first install it on your system. Please follow the official installation instructions in the PDM documentation. Once installation is complete, you can check the PDM version by running the pdm --version
command in your terminal:
$ pdm --version
Initializing a Project with PDM
To initialize a new Python project with PDM, we’ll use the pdm init minimal
command. This command creates a minimal project with a pyproject.toml
file containing the project’s basic information.
First, let’s create a new directory for our project named my-project
and move into it:
$ mkdir my-project && cd my-project
Next, initialize the project with the pdm init minimal
command:
$ pdm init minimal
During initialization, PDM will ask you several questions about the Python interpreter to use, project name, version, license, author, etc. Once the questions are answered, you’ll obtain a minimal project structure similar to this:
-
my-project
-
.venv
-
bin
-
lib
-
.gitignore
-
pyvenv.cfg
-
-
pyproject.toml
-
.pdm-python
-
Let’s explain the files generated during project initialization:
pyproject.toml
: This file is the heart of your project. It contains project metadata (name, version, description, etc.), as well as dependencies and custom scripts..pdm-python
: This file stores the path to the Python interpreter used for this specific project..venv/
: The virtual environment directory that isolates your dependencies from the global system.
Dependency Management with PDM
One of PDM’s main features is simple and efficient dependency management. Here are some common commands you’ll use to manipulate dependencies:
1. Installing Dependencies
To add a new dependency to your project, use the following command:
$ pdm add <package_name>
Example: Let’s add Django to our project:
$ pdm add django
This command automatically updates the pyproject.toml
file and locks the versions in pdm.lock
. After installation, the pyproject.toml file is updated with the added dependency:
[project]name = "my-project"version = "0.1.0"description = "Default template for PDM package"authors = [ {name = "<your name>", email = "<your email>"},]readme = "README.md"license = {text = "MIT"}requires-python = ">=3.12"dependencies = [ "django>=5.1.4",]
[tool.pdm]distribution = false
2. Installing Development Dependencies
To add development-specific dependencies, use the -dG <group_name>
option:
$ pdm add -dG dev django-debug-toolbar
$ pdm add -dG test pytest-django
This adds django-debug-toolbar
and pytest-django
to separate sections of dependencies in pyproject.toml
, allowing you to separate tools needed only for development and testing. The pyproject.toml
file now includes a section for development dependencies and another for tests:
[project]name = "my-project"version = "0.1.0"description = "Default template for PDM package"authors = [ {name = "<your name>", email = "<your email>"},]readme = "README.md"license = {text = "MIT"}requires-python = ">=3.12"dependencies = [ "django>=5.1.4",]
[dependency-groups]dev = [ "django-debug-toolbar>=4.4.6",]test = [ "pytest-django>=4.9.0",]
[tool.pdm]distribution = false
3. Updating and Removing Dependencies
Update a dependency:
$ pdm update <package_name>
Remove a dependency:
$ pdm remove <package_name>
PDM Scripts
With PDM, you have the ability to manage scripts flexibly and powerfully, which simplifies common or complex tasks in your projects. Inspired by systems like npm run, PDM allows you to define and run scripts directly in the context of your project while integrating local dependencies. Here’s an overview of the features and types of scripts PDM offers. Again, I invite you to consult the official documentation for more details.
Arbitrary Scripts
PDM allows you to run any command or script in an environment that automatically includes your project’s packages. For example:
$ pdm run python manage.py runserver
Here, the command is executed with all project dependencies already available, without manually managing a virtual environment.
Single File Scripts
Introduced in version 2.16.0
, single file scripts allow you to directly include metadata in your Python files. These metadata define specific dependencies or parameters needed to run the script. Example:
# /// script# requires-python = ">=3.12"# dependencies = [# "httpx",# ]# ///
import requests
response = requests.get("https://jsonplaceholder.typicode.com/todos")todos = response.json()print(todos)
By running this script with:
$ pdm run script.py
PDM will automatically create a temporary environment with the specified dependencies (httpx
), execute the script, then clean up the environment when finished. You can also use the --reuse-env
option to preserve the temporary environment and avoid recreating it for each execution.
Custom User Scripts
You can define scripts directly in the [tool.pdm.scripts]
section of the pyproject.toml
file. These scripts are perfect for automating development and testing tasks without requiring your project to be installed as a Python package. Here’s an example:
[project]name = "my-project"version = "0.1.0"description = "Default template for PDM package"authors = [ {name = "<your name>", email = "<your email>"},]readme = "README.md"license = {text = "MIT"}requires-python = ">=3.12"dependencies = [ "django>=5.1.4",]
[dependency-groups]dev = [ "django-debug-toolbar>=4.4.6",]test = [ "pytest-django>=4.9.0",]
[tool.pdm.scripts]dev = "python manage.py runserver"mm = "python manage.py makemigrations"migrate = "python manage.py migrate"test = "pytest tests/"
[tool.pdm]distribution = false
In the terminal, you can run this script with:
$ pdm run dev
Any additional arguments will be automatically added to the command. For example:
$ pdm run dev --version
Pre and Post-execution Scripts
PDM also supports running scripts before and after a main script, allowing you to compose complex and automated pipelines. For example:
[tool.pdm.scripts]pre_compress = "echo 'Preparing for compression...'"compress = "tar czvf compressed.tar.gz data/"post_compress = "echo 'Compression completed!'"
In this example:
pre_compress
displays a message indicating preparation is in progress.compress
executes the command to compress files from thedata/
folder into acompressed.tar.gz
archive.post_compress
displays a message confirming that the compression operation is complete.
To see all available scripts in your project, use the pdm run --list
command.
Locking Versions and Exporting requirements.txt
PDM provides powerful tools to manage and share your project’s dependencies consistently. Here’s how to lock dependency versions and generate a requirements.txt
file for maximum compatibility with other tools.
Locking Dependency Versions
When working on a collaborative project, it’s essential that all developers use the same dependency versions to ensure a homogeneous environment. PDM facilitates this task with the following command:
$ pdm lock
This ensures that your application or service will function identically, regardless of the machine on which it is run.
Exporting requirements.txt
Although PDM uses its own lock file (pdm.lock
), it is sometimes necessary to generate a requirements.txt file for tools or environments that do not yet support PDM, such as certain CI/CD systems or cloud platforms.
To export a requirements.txt file:
$ pdm export -f requirements --without-hashes
This generates a requirements.txt
file containing all locked dependencies.
Conclusion
In summary, PDM
emerges as a modern and efficient Python project management tool, offering features that considerably simplify and improve developer workflows. By integrating concepts inspired by the JavaScript ecosystem (like npm
) and adopting modern Python standards (PEP 582, PEP 621), it redefines how dependencies and environments are managed.