Why are there so many Python packaging tools?
Python existed for nine years before its first package manager, distutils, shipped with Python 1.6 in 2000. Distutils could build and install packages, but it couldn’t download dependencies, resolve version conflicts, or create reproducible environments. Setuptools arrived in 2004 to patch some of those gaps, and pip followed in 2008 to handle installation. For the next decade, that stack was the only realistic option.
Why setuptools held a monopoly
Setuptools was structurally entrenched. pip assumed setuptools as the build backend. setup.py scripts could run arbitrary code at install time, which meant packages depended on setuptools behaviors that no specification documented. Switching to an alternative build tool meant your package couldn’t be installed by most of the ecosystem.
This created a specific set of unsolved problems: no way to declare build-time dependencies (you had to hope setuptools and its plugins were already installed), no standard for specifying project metadata outside of executable Python code, and no mechanism for version locking across platforms.
What PEP 517 changed
PEP 517 (accepted in 2017) defined a standard interface between installers like pip and build backends. Instead of hardcoding setuptools, pip could now ask any compliant backend to build a package. This single change broke the monopoly.
Flit was one of the first to take advantage, offering a minimal build backend that replaced setup.py with declarative metadata in pyproject.toml. Hatch followed with its own backend (hatchling) plus environment management. PDM added PEP 582 local package support and lockfile generation. uv took a different approach, reimplementing the installer and resolver for speed while staying compatible with existing backends.
Each tool exists because it solves a problem the others don’t prioritize. Flit optimizes for simplicity: publish a pure-Python package with minimal configuration. Hatch handles multi-environment testing and version management. uv makes dependency resolution and virtual environment creation fast enough that they stop being bottlenecks.
Why no one has “won”
Python spans scientific computing, web applications, machine learning, and scripting. A data scientist installing PyTorch with CUDA bindings has different packaging needs than a web developer deploying a Django app. No single tool has unified these use cases, though uv has absorbed more of them than any previous alternative.
The lack of a single answer frustrates newcomers, but Python’s packaging standards (PEP 517, PEP 621, PEP 751) mean that packages built by one tool can be installed by another. The competition happens at the workflow level, not the package format level.