Published on

What's New In PDM 2.0?

Reading Time

9 min read



PDM has released 2.0.0 recently, it is mostly complete feature-wise. This post lists and illustrates the important new features, and more details can be seen in the changelog.

Virtualenv becomes the default

When PDM was first created, it was advertised as a package manager supporting the PEP 582 package structure. However, after two years of waiting, the PEP 582 is still in Draft status and has been slow to progress. Although it was a hit at the beginning and attracted a large number of initial users, this has been an impediment to being accepted by the mainstream, limiting its promotion. Also, virtual environments have better support in IDEs and tools. Now I hope that PDM is not only a project of personal interest, but also a package manager that supports the latest Python packaging standards and aims at general developers. So in 2.0, we made virtualenv the default setup for the project.

  1. In pdm init, if you choose a non-venv interpreter, PDM will ask if a new virtual environment needs to be created. If answered yes, it will create a new one and use it as the project environment. Otherwise, PEP 582 will still be used.
  2. When you clone an existing project and execute pdm install(or other commands) for the first time in the project, PDM will check if __pypackages__ directory exists in the project root1 and use PEP 582 layout if so. Otherwise, a new virtualenv will be created and install packages into it later.

As much as possible, we make sure that the old project does not break, but only the default way of the new project changes. In the documentation, PEP 582 has also been moved from its most prominent position on the front page to a subpage. In another word, PDM still supports PEP 582, just not in the default way.

Work with other PEP 517 backends

Although PDM is shipped with its own backend2 pdm-pep517, it indeed isn't locked-in with any backend. You can still use backends like flit-core, hatchling and setuptools, as long as it supports reading metadata from PEP 621. You can even mix PDM with other package managers. For instance, you can use flit to build and package your project and use PDM only as a dependency management tool, since the former does not have dependency management capabilities.

This is the great benefit of embracing standards--tools can co-exist with each other, each doing its own job, as long as they follow the standards.

Editable packages are no longer allowed in project dependencies

Originally, in [project]:dependencies in pyproject.toml you could include something like -e ./mypackage, -e git+ that will be installed in editable mode. However, this is not compliant with the PEP 631 specification, in 2.0 we therefore no longer support this kind of dependencies, and existing editable packages will trigger a warning on installation.

But don't worry, you can still include editable packages in [], because they are actually only useful in development.

The PDM global configurations are relocated

Originally, the global configuration of PDM existed under ~/.pdm, but in 2.0 they will be placed under $CONFIG_HOME. The specific values are:

  • Linux: $XDG_CONFIG_HOME/pdm (~/.config/pdm for the most of time)
  • MacOS: ~/Library/Preferences/pdm
  • Windows: %USERPROFILE%\AppData\Local\pdm

You need to do a one-time migration (Linux for example).

$ cp -r ~/.pdm/* ~/.config/pdm/

Thanks to @noirbizarre for the contribution.

Add pdm publish command

Yes, this is a feature that many users would like to have, and we've finally added it in PDM 2.0! Execute pdm publish and PDM will automatically package the project and upload it to PyPI. Of course, before you do that, you need to configure the repository credentials.

$ pdm config repository.pypi.username <username>
$ pdm config repository.pypi.password <password>

The UI is richified

PDM 2.0 changed the UI rendering from click + halo to rich, which provides a one-stop experience and flexible configuration to build a powerful and beautiful UI. The UI remains mostly the same, but the rich implementation is simpler and less buggy.

pdm add pdm list

Thanks to @daylinmorgan for the contribution.

Abandon the usage of pip's internals

PDM 1.x used to use some pip internal APIs to find and download packages, but pip is not made as a library and it follows the CalVer release, so even in patch upgrades the API may change and cause PDM to break. Previously, PDM could only limit the version range of pip in the dependencies, but the problem is that pip, as a basic tool, may have been patched in different Linux distributions that may not be compatible. So we completely abandoned the usage of pip's internals and instead built our own wheel unearth for the same work. This will increase stability and also make things easier for downstream packagers.

The enhanced user scripts

In previous versions PDM we have added support for user scripts([tool.pdm.scripts], like scripts in package.json). In version 2.0, we have continued to add many features that to make this system more powerful and flexible.

Thanks to @noirbizarre for the contribution.

Composite script

You can use composite scripts to combine multiple scripts that will execute sequentially, and if any one of them fails, the entire composite script fails.

lint = "flake8"
test = "pytest"
all = {composite = ["lint", "test"]}

Running pdm run all will execute lint and test.

User scripts as root commands

If you have a script named start, then pdm start and pdm run start will also execute this script, as long as the script name does not conflict with other commands.

Add more hooks

Hooks are callback actions that are triggered before and after a specific event happenning in PDM, which can benefit the plugin development. In PDM 2.0 we have added the following hooks:

  • pre_publish/post_publish: triggered before and after publish
  • pre_run/post_run: triggered before and after a pdm run execution
  • pre_script/post_script: triggered before and after an individual script is run
  • post_use: triggered after the Python interpreter is changed by pdm use.

Please refer to documentation for details on how to use them.

Thanks to @noirbizarre for the contribution.

Add --skip to skip hooks or scripts

With so many hooks, sometimes you don't want them all to fire. You can use the --skip option to skip some hooks or scripts.

# 跳过 pre_install
pdm install --skip pre_install
# 跳过 pre_publish 和 post_publish
pdm publish --skip pre_publish,post_publish
# 跳过所有 post_* 以及 pre_start 钩子和脚本
pdm run --skip:post --skip pre_start start

Thanks to @noirbizarre for the contribution.


I received a lot of feedbacks and contributions during the development of PDM 2.0, especially from @noirbizarre (who is the author of flask-restplus). He contributed most of the user script functionality. Here I would like to post his comments about PDM in a discussion:

I think the PEP-582 support even if opt-in is already an argument.

Given I started using PDM recently after a painful year (maybe 2) looking for a decent solution (Python project management and packaging have been hell recently):

  • it just work, no tricks need, no special version (seems logical but just try any other and you will understand)
  • scripts support (game changer to me, Poetry only have it through poet and Pipenv ones are limited (no env, no shell chaining, no composition...)
  • supports PEP-621: Poetry and Pipenv don't, setuptools has a just recently experimental support
  • lock file support + pnpm-like cache
  • editables support (only project so far having both PEP-621 and editables support)
  • single tool batteries included (setuptools and flit don't cover the full workflow, poetry is going under heavy refactoring leading to 2 non-stable codebases, and Pipenv does not support packaging and extensions are very limited, mostly IDE integration)
  • powerful extensions support + dynamic ecosystem: (I can attest it, I contributed a lot lately, everything has been reviewed and accepted, very quickly and the tone was good/kind). I can add transparent and realistic roadmap
  • great doc !
  • extensive test suite
  • have I said it just work 🎉

And I'm sure some points are missing from the list.

To make it clear: PDM is as of today the only Python project management and packaging tool covering the full lifecycle, supporting all recently published PEP on packaging, supporting lock and being able to handle both libs and apps. (And this this why I'm currently in the process of migrating all my projects on PDM)

Install and try the new PDM version

Welcome to use and test the new PDM version.

If you install with

curl -sSL | python3 - --prerelease
# Or Windows powershell:
(Invoke-WebRequest -Uri -UseBasicParsing).Content | python - --prerelease

If you install with pipx:

pipx install --pip-args="--pre" pdm
# or update the installed version
pipx upgrade --pip-args="--pre" pdm


  1. You only need to upload an empty __pypackages__ to the source code management and ignore the packages in it.

  2. In Python packaging, a backend is the tool that reads the metadata to build a package(like setuptools), and a front-end is the tool that provides the user interface to modify the metadata(like pip)