User experience with porting off setup.py

A few quick points (since I’m on mobile):

You can’t build extension modules with setuptools without a setup.py. It’s the only way to add them. Same for customizing commands. Or doing logic at build time other than a small predefined set. And that’s fine, setup.py is and always will be the dynamic build file for setuptools.

If you can move everything to pyproject.toml, then you can delete the setup.py. Otherwise it stays around. Both are fine. In fact, we don’t really care if you move static config or not (though it makes it easier to analyze, say by GitHub’s dependency graph, etc). From the packaging standpoint, you’ve done what we asked you to do when you add a three line pyproject.toml.

There is no built-in packaging solution. Distutils was added in python 2.0 many years ago, and has been removed in 3.12. There’s nothing special about setuptools anymore, it’s no longer added by default. It was added by default in the past, largely because it modified the built-in distutils and made it usable, it turns out it’s really hard to ship a packaging solution that you can’t update regularly. Setuptools actually forces the third party distutils.

This is great, because it means that packages can now choose the build back end at best suits them, and no longer say that they are trying to avoid adding independency, as they all add a dependency. Like Poetry? Use it! (I don’t since they don’t support PEP 621 yet, PDM is better IMO) For compiled packages, scikit-build-core and meson-python have been revolutionary. It was fun sitting with people at the SciPy sprints this year and converting 800+ line setup.py’s into <20 line CMakeLists.txt and a simple PEP 621 pyproject.toml. And now support more platforms like WASM that they didn’t before. IMO, that’s the path forward for compiled projects - setuptools/distutils really wasn’t designed for complex C++ builds. That’s why NumPy had 13,000 LoC dedicated to building before they moved to meson-python in 1.26.

If you have a pure Python package, and you can perform your configuration entirely statically, which is a huge number of packages, then it really doesn’t matter which one you pick. Hatchling is faster, smaller, and simpler than setuptools, and provides better error messages. Setuptools has a lot of legacy to deal with, including still internally being structured as a distutils plugin, while hatchling doesn’t. But if you like setyptools better, use that. You should just not be feel pressure to use it because it is the “default” or “built-in”. Flit-core has 0 dependencies and is 10-20x less code than setuptools, but has a much harder time getting file include/exclude right without some configuration. These are all absolutely fine choices, and it’s kind of the point that packages can select whatever they want, whatever works best for them, everybody is free to innovate and make packaging better. (And there is a lot of room for improvement!)

Check your favorite packages or dependencies, many of them have already moved to something modern. Of the 50 or so packages I help maintain, about 3 still use setuptools, most the others use hatchling or scikit-build-core. All of them have pyproject.tomls, of course. :slight_smile: Many of the pypa projects use flit-core or hatchling.

13 Likes