I have released zc.buildout 5.3.0a1. If you like buildout and would like to see it modernized, this is a first step with a performance improvement.
## TLDR
zc.buildout 5.3.0a1 adds opt-in uv support: buildout now delegates downloads and version resolution to uv instead of pip. Enable it with installer = uv in the [buildout] section; pip remains the default. Expect a large speedup — for Plone on my Mac M2, from 7 minutes down to under 2 on a cold cache, and seconds on a warm one.
## How it came about
Besides adding uv support, my goal was to learn how to let the agents write all the code for me. The work happened in three steps:
1. rewriting the legacy doctest test suite into a pytest suite;
2. improving the verification harness for the agents' work: lowering cyclomatic complexity, adding type annotations, reducing the use of Any;
3. replacing pip and setuptools.package_index usage with uv only.
I used the pstack poteto-mode skill as a driver to build a verify-buildout skill. I left every step to the agents through a series of prompts.
The first step ported the legacy doctest suite to pytest. That alone gave the test suite a solid performance boost thanks to pytest-xdist parallelization. The two suites have very similar coverage. From then on, development ran against the pytest suite; I ran the legacy suite only to confirm there were no regressions.
The second step made the codebase easier for agents to refactor. Reducing cyclomatic complexity made the code easier to understand. It exposed cases that only integration tests might have covered, so the agents added unit tests for them. Ruff linting raised the overall quality of the code. The agents added type annotations across the codebase to make the semantics explicit. They also made a deliberate effort to shrink the use of the Any type. They split large files into shorter ones that they can read and write more cheaply. All this tooling supplements the test suites. Together they form a verification harness that gives me confidence few regression slipped through.
With the codebase cleaned up, it was time for the heart of the move: adding uv support, to take advantage of the speed of uv's version resolution and parallel downloads.
That took two refactorings: replacing the pip install subprocesses with uv ones, and delegating version resolution to uv instead of setuptools.package_index. The delegation also reduces how many subprocesses buildout launches.
## What uv support actually does
In uv mode, a single `uv pip compile` handles package discovery and version resolution. After the environment check, uv resolves everything still open in one pass, with the buildout's develop projects riding along as overrides. One batched `uv pip install` then installs everything. The per-requirement pip subprocess fan-out is gone.
I kept uv support opt-in, behind a new `buildout:installer` option. Add `installer = uv` to the `[buildout]` section of your config, or override it on the command line with the usual assignment syntax, for example `bin/buildout buildout:installer=uv`. The default value remains `pip`. That makes it easy to compare the two installers and debug any regression that slipped through anyway. uv mode requires uv 0.12.11 or newer, and `uv` is now a declared dependency of zc.buildout; the pip code path is unchanged.
## What else is in the release
- New `--interpolated` option on the `query` and `annotate` commands: print values after applying the `${...}` substitutions, exactly the way recipes see them. Raw values remain the default output.
- Repeated buildout runs are faster: buildout no longer reinstalls the sources listed in the `develop` option on every run. It only redoes an editable install when `setup.py`, `setup.cfg` or `pyproject.toml` is newer than the egg-link.
- Bug fixes worth mentioning: spurious uninstall/reinstall cycles of parts whose recipe resolves to a develop egg; offline mode now reuses wheel-installed distributions; a `TypeError` that made zc.buildout unimportable on Python 3.9; and several Windows fixes, including local package indexes spelled as drive paths now correctly converting to `file://` URIs.
For the development infrastructure aficionados: I rebuilt CI and the development environment around devenv and Dagger. `dagger call ci` runs the whole CI job table locally or in containers. CI also gained a parallel uv test set: it reruns the legacy suite through the uv install pipeline, across the full platform matrix — including Windows — and across a matrix of uv versions.
## Tests done with the new release
I have run Plone 6.2.0 through 6.2.2, 6.1.0 through 6.1.5, and 6.0.11 through 6.0.15 under Python 3.10. I also tested the 6.2.x series with 3.14, and 6.1.x with 3.13.
In each case, downloading from scratch (nothing cached) shows a good performance boost. On my M2 with a bad internet connection, installation went from roughly 7 minutes with pip down to 1 minute 45 seconds.
As soon as the uv cache is hot, it gets much faster: under 10 seconds to install any of those Plone setups.
This is still an alpha. I would love to hear from you what works and what breaks for you. Enjoy.