Setuptools follows through on a deprecation and breaks everything
Four years after announcing a deprecation, Setuptools shipped it under a new major version number. And then vast swaths of the Python ecosystem broke.
Setuptools broke vast swaths of the Python ecosystem this week. They released a new version dropping support for hyphenated and uppercase config names. This caused breakages across the Python ecosystem.
A user ran a query across Github and discovered that more than 12,000 packages had a single key that would produce a value. As the errors kept rolling in, it was clear that a ton of important packages broke with this release:
The top-level bug was filed for
ansible-vault
, which manages secrets for the Ansible project.pyspark, the Python interface for Apache Spark
Datadog’s python wrapper
And so many more!
Further complicating matters, it was hard to fix. People tried pinning setuptools and found that it did not work consistently. Users typically had to do several steps in tandem like clear their pip
cache, pin a specific version of setuptools, and disable build isolation within pip
to get their code to work. But since Github issues don’t surface important information buried within the comment thread, people couldn’t find the resolution steps and kept reporting more and more breakages. Further complicating matters, many of the broken packages were (a) actively in use like ansible-vault
, and (b) either unmaintained or years have passed since the last release.
So why did Setuptools do this? On its face, they took an approach used by many open-source libraries.
They followed proper semver standards and released a breaking change under a new version.
If you didn’t pin your dependencies, that’s on you.
While it’s ultimately wrong, I have some sympathy for this argument? A goal of semver is to give projects the option of introducing backwards-incompatible changes. However, Setuptools is clearly under constraints that normal projects don’t face.
They are load bearing on the entire Python ecosystem
Other load-bearing tools pull in the unpinned version of Setuptools
Software engineering is becoming “command runners all the way down,” and it’s difficult to configure all of them.
What do I mean by #3? Over the past 2 decades, programming environments have trended towards being collections of compilers and command runners instead of just monolithic compilers. If you didn’t know anything about frontend development, but knew how to develop server software, you might think “You will have between 1 and 3 compilers that parse HTML, CSS, and JavaScript and produce the final bundles that are shipped to the user.”
But let’s consider someone who wants to use a modern frontend stack and write Tailwind in SCSS and import their stylesheet files into their JSX components in their React project. They’ll have a pile of compilers that will, to some approximation and depending on configuration…
Construct the Tailwind CSS bundles and perform tree shaking.
Convert the SCSS syntax into CSS.
Parse the stylesheets and make the parsed object available as an import to JSX files.
Convert React’s JSX syntax into JavaScript.
And finally, a bundler which will package sources and the outputs of all of the compilers.
This whole ecosystem then gets wrapped in command runners like npm that make it convenient to invoke the underlying compiler ecosystem. Then you might wrap your NPM project in some type of CI provider that ensures that the build succeeds and produces the correct assets. Your project is not just one compiler, but instead layers of compilers and command runners.
Of course this is true in Python too. Python provides distutils for building packages, but it is underpowered. For example, it doesn’t have a way to declare third-party libraries as dependencies. So the Python community developed Setuptools to narrow some of the gaps. And now we have Poetry (which wraps Setuptools) as an alternative build system, Tox (which wraps Setuptools) as a way to test Python code across Python versions. And on top of that, you have things like CI runners that execute those tools. The Python ecosystem is turning into a pile of compilers and command runners.
And we haven’t even gotten into the issue of “every single one of these layers in the supply chain must have third-party dependencies that nobody has read very carefully. I wonder if THAT is going to be a problem?”
Either way, the Setuptools team reverted the issue. In 2026, they will try again. Let’s hope that the Python community starts pinning the Setuptools version they depend on before then.
They should have kept the breaking change. Or at least, un-revert in a month or so. We need shockwaves to become robust.