A few years back, I started using poetry to manage dependencies for all my Python projects. I ran into some minor issues early on but haven’t had any problems recently and prefer it to any of the other dependency management / packaging solutions I’ve tried so far.
Recently, I’ve started hearing about pdm and how it’s the bee’s knees.
I did a search for “pdm vs poetry” and didn’t find much, so I thought
I’d play around with pdm
a bit and write something myself.
Creating a new project
Both tools have an init
command. Create a new directory, cd
into it,
run <tool> init
, and follow the prompts.
One difference here is that poetry
will prompt for more package
metadata and allow you to define your dependencies at this stage.
The pyproject.toml
generated by pdm
is pretty bare, so you’ll have
to fill in a bunch of stuff like the project name, version, etc.
pdm
will also ask if you want to create a virtualenv
at this stage.
If you choose y
, it will create a virtualenv
in ./venv
. If you
choose n
, it will use PEP 582 mode (more on PEP 582 below).
Converting an existing project from poetry to pdm
pdm
has an import
command that you can run on an existing
pyproject.toml
. It will keep all of your existing config, except
it will overwrite the [build-system]
section.
If you’re using the [tool.poetry.group.dev.dependencies]
section to
define your dev dependencies, pdm
won’t recognize it, so I’d advise
changing it back to [tool.poetry.dev-dependencies]
to avoid issues
with that.
pdm
also didn’t import local develop
dependencies correctly. It
converted poetry
dependencies like this:
some-package = { path = "../some-local-package", develop = true }
to:
[tool.poetry.dev-dependencies]
dev = [
"some-local-package @ ",
]
This causes pdm install
to throw a cryptic TypeError
. To fix it, you
have to remove the @
and change the spec to:
"-e file:///abs/path/to/some-local-package"
Differences
-
poetry init
prompts for more metadata and creates a cleaner initialpyproject.toml
vspdm init
. -
poetry
uses TOML syntax to define dependencies whilepdm
uses pip-style strings. I preferpoetry
’s approach:# poetry [tool.poetry.dependencies] django = ">=4.1.4" markdown = ">=3.4.1" [tool.poetry.group.dev.dependencies] some-local-package = { path = "../some-package", develop = true } # pdm [project] dependencies = [ "django>=4.1.4", "markdown>=3.4.1", ] [tool.pdm.dev-dependencies] dev = [ "-e file:///abs/path/to/some-local-package", # NOTE: This works only if you're using specific build backends "-e file:///${PROJECT_ROOT}/../some-local-package", ]
-
As shown above,
poetry
lets you use npm-style^
and~
dependency constraints or pip-style>=
constraints. I prefer the former for applications and the latter for libraries. -
As shown above,
poetry
makes it easy to specify relative paths to local dependencies.pdm
will let you do something similar using the injectedPROJECT_ROOT
var, but only if you’re using the correct build backend.
-
poetry
has less options with regard tovirtualenv
vs__pypackages__
and build backends. I’m not sure this is better per se but I found myself scratching my head a bit more withpdm
(although I admit this could be due to being more familiar withpoetry
). -
The size of the directory where packages are installed is slightly smaller when using
pdm
. This is becausepoetry
installspip
,setuptools
, andwheel
into the virtualenv whilepdm
doesn’t.poetry
also seems to pre-compile all Python modules up front whilepdm
doesn’t.
Dependency resolution
I didn’t notice a huge difference in the speed of dependency resolution or initial installation (pre-cache).
PEP 582
Using PEP 582 mode with pdm
requires a bit of setup since Python
doesn’t support it out of the box, but it’s not too bad (and isn’t
specific to pdm
). There’s a PEP 582 page on the pdm site that tells
you how to do this.
It’s pretty straightforward, but I did run into one issue when running
pdm --pep582
in fish. It outputs some lines that export environment
variables using set -x
, but I had to change them to set -gx
to get
everything to work.
The other thing you might want to do is put __pypackages__/3.x/bin
in
your $PATH
when you cd
into a project directory. I wrote some
fish scripts to do this, but you could just use direnv.
Conclusion
I’ll probably keep an eye on pdm
, but poetry
seems a bit more
polished currently. If you’re already using poetry
, I don’t see any
reason to switch. If you haven’t gone down this particular rabbit hole
yet, I’d probably recommend poetry
at this point.
The most interesting thing about pdm
to me is the PEP 582 /
__pypackages__
support. After playing with it a bit, I do think it
would be a good addition to Python.
Comments, Corrections, Suggestions
This blog doesn’t have a comment system, but you can send feedback via the contact form on the home page.
NOTE: I originally published this on 23 December, but there were a couple of errors. This version removes/corrects those errors and does more of an actual comparison.