Python Package Manager Comparison 📦

This article started as a random thought and then this post on Mastodon.

If you haven't read Hypermodern Python from 2020, it is a good companion to this article and apparently will be a released as a book in 2024.

Introduction

It feels like Python packaging has changed pretty substantially over the past few years. When I started creating Python packages the preferred approach was with setup.py and (what felt like) a myriad of other random files that were needed like MANIFEST.in, setup.cfg, etc. I basically found a guide once and then copied and pasted files around once I seemed to get something working.

I was an early adopter of pipenv with the primary appeal being lock files which promised consistent deployments. My early excitement got me burned multiple times and I looked around some more.

I remember Poetry being the cool, new upstart at the time. It completely eschewed setup.py, requirements.txt, and other disparate files -- it had one pyproject.toml file to configure a project, developer-friendly tooling to add dependencies, a lock file for consistent deployments, and an algorithm to make sure that a tree of dependencies was all compatible with each other (which was not possible with that version of pip at that time).

I was in love. There were definite rough patches and there were a few bugs that caught me here or there, but overall Poetry has been my go-to for a while.

However, in the past few years, Python packaging seems to be going through a new era of innovation. pip released a dependency resolver in 2020 that now complains when dependencies conflict. Also in 2020, PEP 621 and PEP 631 standardized pyproject.toml as the new normal for Python packages instead of setup.py. PEP 517 and PEP 660 created standards for Python build systems.

Based on those PEPs, Poetry isn't the only forward-thinking package manager anymore. pipenv is still around (and I presume more stable at this point!), but Hatch, PDM, and Rye all promise a pleasant developer experience when creating Python packages.

Benefits

Most of these package managers are of the "all in one" mentality and provide similar benefits: adding dependencies from the command-line, lock files, building, and publishing packages. If you want to do this manually Packaging Python Projects lays out the steps necessary. Reducing the number of tools required to create new Python packages means there is only one place to look for documentation and keep up to date.

Poetry

My first modern Python package manager. I would say the documentation is still the best available, although it might be too "designed" for some. It does a lot -- maybe too much? -- but, in my opinion, it pioneered a lot of features that are now expected in other Python package managers. I get the feeling it sometimes pushes forward without waiting for official PEPs, although I think that has changed over the past few years.

One annoying feature lacking in Poetry that is available in every other option is the ability to define "scripts". For example, poetry run dev as an alias for poetry run manage.py runserver 0:8000. That is not available in standard Poetry, although I use poethepoet (a Poetry plugin) to provide that functionality.

Hatch

Hatch never deviates from the Python PEP standards and brings a few innovative features to the table, including being able to group dependencies and scripts into custom environments. The hatch new [project] command is surprisingly opinionated has settings for ruff (my new favorite Python tool), pytest, and coverage. I'm here for all of the tool choices, but I would be lying if I did not say it was a little surprising.

Under the PyPA GitHub organization which probably gives it more of a sheen of officialdom than other tools.

PDM

PDM for some reason feels like the sleeper option compared to the rest. I have not used it too much other than to set up a new project and play around with it a little bit. Dan Sloan chimed in on Mastodon pointed out that PDM follows all PEP standards, good cross-platform lock files, and a useful pdm export to output a standard requirements.txt file.

Rye

rye seems to have had a quick and meteoric rise. It was developed by the creator of Flask, Armin Ronacher, based on his opinionated approach to building Python packages. It reminds me of black a little bit in which a respected Python developer made an opinionated tool and it caught fire in the community. Rye has an overview of its philosophy.

Rye has multiple notes that it is experimental and not production-ready, although I did not see any glaring issues with my simple testing. Rye is a little different than the other options because it mostly consolidates a set of other tools (pip, pip-tools, etc) into a particular workflow. Another is that it handles Python versions directly -- Poetry recommends using pyenv to deal with multiple Python environments.

GitHub Stats

GitHub stats are not particularly useful, but the number of stars do give some indication of the community engagement with a particular tool (current as of 2023-11-12).

Conclusions and takeaways

Personally I have multiple libraries using setup.py and Poetry, one using Hatch, and now one using Rye. The next one might use PDM. 😎 I like trying new tools to see how they work and to improve my workflow.

One could look at all of the options for Python packaging and be frustrated by the paradox of choice. I understand that viewpoint, but I also appreciate that all of these tools are making different trade-offs and pushing the state of the art forward in their own ways. Maybe at some point in the future there will be one "approved" Python package manager ala NPM or Cargo, but I am doubtful. There are pros and cons with multiple options, but I tend to think its useful overall.

I personally have not used each package manager extensively. But, I have started a new project from scratch with each, did some simple tasks, and read through all of the documentation. Each project is relatively mature, so you can just choose any of the options and you would be fine. Standardizing on pyproject.toml means that the conversion from one package manager to another is usually just updating some TOML. So, choose the package manager that speaks to you (or try them all!) and improve your developer experience henceforth. 🛠️


Related Content

Hi, I'm Adam 👋

I've been a backend programmer for ~20 years in a variety of different languages before I discovered Python 10 years ago and never looked back. alldjango includes all the hard-won experience I've gained over the years building production-scale Django websites.

Feel free to reach out to me on Mastodon or make a GitHub Issue with questions, comments, or bitter invectives.

All code is licensed as MIT.

DigitalOcean Referral Badge

   


Made with 🤟 and built with Coltrane 🎵


© Adam Hill