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.
- 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. - 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+https://github.com/psf/requests.git@main
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 [tool.pdm.dev-dependencies]
, 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.
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.
[tool.pdm.scripts]
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 publishpre_run/post_run
: triggered before and after apdm run
executionpre_script/post_script
: triggered before and after an individual script is runpost_use
: triggered after the Python interpreter is changed bypdm 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.
Acknowledgements
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
andPipenv
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
andflit
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 install-pdm.py
:
curl -sSL https://raw.githubusercontent.com/pdm-project/pdm/main/install-pdm.py | python3 - --prerelease
# Or Windows powershell:
(Invoke-WebRequest -Uri https://raw.githubusercontent.com/pdm-project/pdm/main/install-pdm.py -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
Footnotes
You only need to upload an empty
__pypackages__
to the source code management and ignore the packages in it. ↩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(likepip
) ↩