You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@priya-sundaram-dev Adding Polars (#15397) has substantially slowed our build process.
Do we really need to build Polars from scratch on every pull request?
Is there some way to use uv cache to speed up the installation process?
Should we cargo install polars instead of installing it with uv?
Good catch — and the cause is specific. Polars does ship prebuilt wheels, so normally there's no Rust compile at all. The problem is our interpreter.
As of 1.44.2 the binary lives in polars-runtime-32, and every wheel it publishes is tagged cp310-abi3 (see the uv.lock entries #15397 added). abi3 / stable-ABI wheels are not loadable under free-threaded CPython — the free-threaded build has its own ABI and pip/uv won't match an abi3 tag against a 3.14t interpreter. Since our .python-version is 3.14t, uv finds no compatible wheel for polars and falls back to building the sdist, i.e. compiling Rust from scratch on every run. That's the slowdown.
So, to your three questions:
Build from scratch every PR? Only because of 3.14t. On a standard (GIL) CPython the cp310-abi3 wheel installs in seconds.
cargo install polars? That won't help — polars isn't a standalone cargo binary crate; it's a Rust library plus Python bindings. cargo install has nothing to install here.
uv cache? Worth doing regardless (enable-cache: true on astral-sh/setup-uv, or actions/cache on ~/.cache/uv), but it only caches the result of the source build — the first run per lock change still pays full compile time.
Cleanest fix: run the Polars-using check on a plain-GIL interpreter (a dedicated matrix leg pinned to 3.14, not 3.14t) so it uses the prebuilt wheel, and keep the free-threaded leg for everything else. When polars-runtime-32 starts publishing cp314t free-threaded wheels upstream, we can drop the split. I can open a PR that adds the pinned leg + enables the uv cache if you'd like.
and the latest build run confirms it's live, not cold:
Cache hit for: setup-uv-2-...-pruned-c5f50d16...
Cache restored successfully
Also worth noting: #15397 was closed, not merged, so Polars is no longer in uv.lock — nothing is currently building it from source. The slowdown you saw was the one-time cost on that PR itself: adding Polars changed uv.lock, which invalidated the cache key (it's keyed on the lock hash), so that single run had to build the cp310-abi3 sdist under 3.14t with a cold cache. Since it's closed, that cost is gone.
So there's nothing to enable — it's already on. I'd rather not send a no-op PR.
The only thing a PR could add here is eliminating even that one-time cold-build cost on the rare lock change, by giving the cache a prefix restore-keys fallback so a lock bump restores the previous cache (old built wheels intact) and only re-resolves the delta. setup-uv doesn't expose restore-keys, so that means a small hand-rolled actions/cache block instead of the built-in — which is the bigger change you said you'd rather avoid, and you noted the lock rarely changes anyway. Happy to do it if you decide it's worth it, but as-is I don't think it's needed. Your call.
You're not misunderstanding it at all — you're right, and I was wrong on one fact. I conflated this with #15397. Polars is now a permanent dependency (polars>=1.44.2 in pyproject.toml from the Pandas→Polars migration), so it's genuinely in uv.lock and stays there — it was never removed. Apologies for the confusion.
So your read of #15398 is exactly correct: uv sync --upgrade + relock rewrote uv.lock, and since the cache key is the lock hash (cache-dependency-glob: uv.lock), that relock invalidated the cache and the next build hit a cold cache.
The reason a cold cache is expensive here specifically is the free-threaded runner. .python-version is 3.14t, and polars-runtime-32 only ships cp310-abi3 wheels. abi3 wheels aren't usable under free-threading, so on 3.14t uv can't grab a wheel and builds the ~3 MB Rust sdist from source. On a warm cache that built artifact is restored and it's fast; every lock change pays the Rust build once.
So the cache is already on and already doing its job — there's no "enable the cache" PR to send, which is what I was trying (clumsily) to say. The only thing left is whether to soften that once-per-relock cold build. Two honest options:
Leave it. Relocks are rare, the cost is bounded to one run, and the built-in setup-uv cache handles the common case. Simplest.
Add a restore-keys prefix fallback so a lock bump restores the previous cache (Rust build intact) and only re-resolves the delta instead of rebuilding from scratch. setup-uv doesn't expose restore-keys, so this means a small hand-rolled actions/cache block — the bigger change you'd rather avoid.
Given how seldom the lock changes, my vote is option 1, but I'm happy to send option 2 if you'd prefer the belt-and-suspenders version. And once a cp314t wheel for polars ships, the source build disappears on its own.
The long Polars build times are debilitating, so we will close this issue and revert the migration to Polars until that project delivers free-threaded wheels.
@priya-sundaram-dev Please create a pull request that removes Polars from pyproject.toml and then runs:
% uv sync --upgrade && uv lock
Done — #15404 removes polars>=1.44.2 from pyproject.toml and reruns uv sync --upgrade && uv lock. All 30 polars-related lines drop out of uv.lock with no other version changes, and no source file imports polars, so the revert is clean. That takes the Rust-from-source build off the critical path until upstream ships free-threaded (cp314t) wheels.
Activity
cclauss commentedon Sep 22, 2026
@priya-sundaram-dev Adding Polars (#15397) has substantially slowed our build process.
Do we really need to build Polars from scratch on every pull request?
Is there some way to use uv cache to speed up the installation process?
Should we
cargo install polarsinstead of installing it with uv?priya-sundaram-dev commentedon Sep 22, 2026
Good catch — and the cause is specific. Polars does ship prebuilt wheels, so normally there's no Rust compile at all. The problem is our interpreter.
As of 1.44.2 the binary lives in
polars-runtime-32, and every wheel it publishes is taggedcp310-abi3(see theuv.lockentries #15397 added).abi3/ stable-ABI wheels are not loadable under free-threaded CPython — the free-threaded build has its own ABI and pip/uv won't match anabi3tag against a3.14tinterpreter. Since our.python-versionis3.14t, uv finds no compatible wheel for polars and falls back to building the sdist, i.e. compiling Rust from scratch on every run. That's the slowdown.So, to your three questions:
3.14t. On a standard (GIL) CPython thecp310-abi3wheel installs in seconds.cargo install polars? That won't help —polarsisn't a standalone cargo binary crate; it's a Rust library plus Python bindings.cargo installhas nothing to install here.enable-cache: trueonastral-sh/setup-uv, oractions/cacheon~/.cache/uv), but it only caches the result of the source build — the first run per lock change still pays full compile time.Cleanest fix: run the Polars-using check on a plain-GIL interpreter (a dedicated matrix leg pinned to
3.14, not3.14t) so it uses the prebuilt wheel, and keep the free-threaded leg for everything else. Whenpolars-runtime-32starts publishingcp314tfree-threaded wheels upstream, we can drop the split. I can open a PR that adds the pinned leg + enables the uv cache if you'd like.cclauss commentedon Sep 22, 2026
Please create a PR that only enables the uv cache. We do not break the uv lock very often.
Once we have merged or closed all of our open PRs, we can discuss the two test runs (with and without the GIL).
priya-sundaram-dev commentedon Sep 22, 2026
Quick heads-up before I open anything: the uv cache is already enabled on
masterand is hitting. Theastral-sh/setup-uv@v7step already has:and the latest build run confirms it's live, not cold:
Also worth noting: #15397 was closed, not merged, so Polars is no longer in
uv.lock— nothing is currently building it from source. The slowdown you saw was the one-time cost on that PR itself: adding Polars changeduv.lock, which invalidated the cache key (it's keyed on the lock hash), so that single run had to build thecp310-abi3sdist under3.14twith a cold cache. Since it's closed, that cost is gone.So there's nothing to enable — it's already on. I'd rather not send a no-op PR.
The only thing a PR could add here is eliminating even that one-time cold-build cost on the rare lock change, by giving the cache a prefix
restore-keysfallback so a lock bump restores the previous cache (old built wheels intact) and only re-resolves the delta.setup-uvdoesn't exposerestore-keys, so that means a small hand-rolledactions/cacheblock instead of the built-in — which is the bigger change you said you'd rather avoid, and you noted the lock rarely changes anyway. Happy to do it if you decide it's worth it, but as-is I don't think it's needed. Your call.cclauss commentedon Sep 22, 2026
Python/uv.lock
Lines 1199 to 1225 in 6a2ca68
priya-sundaram-dev commentedon Sep 22, 2026
You're not misunderstanding it at all — you're right, and I was wrong on one fact. I conflated this with #15397. Polars is now a permanent dependency (
polars>=1.44.2inpyproject.tomlfrom the Pandas→Polars migration), so it's genuinely inuv.lockand stays there — it was never removed. Apologies for the confusion.So your read of #15398 is exactly correct:
uv sync --upgrade + relockrewroteuv.lock, and since the cache key is the lock hash (cache-dependency-glob: uv.lock), that relock invalidated the cache and the next build hit a cold cache.The reason a cold cache is expensive here specifically is the free-threaded runner.
.python-versionis3.14t, andpolars-runtime-32only shipscp310-abi3wheels. abi3 wheels aren't usable under free-threading, so on3.14tuv can't grab a wheel and builds the ~3 MB Rust sdist from source. On a warm cache that built artifact is restored and it's fast; every lock change pays the Rust build once.So the cache is already on and already doing its job — there's no "enable the cache" PR to send, which is what I was trying (clumsily) to say. The only thing left is whether to soften that once-per-relock cold build. Two honest options:
setup-uvcache handles the common case. Simplest.restore-keysprefix fallback so a lock bump restores the previous cache (Rust build intact) and only re-resolves the delta instead of rebuilding from scratch.setup-uvdoesn't exposerestore-keys, so this means a small hand-rolledactions/cacheblock — the bigger change you'd rather avoid.Given how seldom the lock changes, my vote is option 1, but I'm happy to send option 2 if you'd prefer the belt-and-suspenders version. And once a
cp314twheel for polars ships, the source build disappears on its own.cclauss commentedon Sep 22, 2026
The long Polars build times are debilitating, so we will close this issue and revert the migration to Polars until that project delivers free-threaded wheels.
@priya-sundaram-dev Please create a pull request that removes Polars from
pyproject.tomland then runs:%
uv sync --upgrade && uv lockpriya-sundaram-dev commentedon Sep 22, 2026
Done — #15404 removes
polars>=1.44.2frompyproject.tomland rerunsuv sync --upgrade && uv lock. All 30 polars-related lines drop out ofuv.lockwith no other version changes, and no source file imports polars, so the revert is clean. That takes the Rust-from-source build off the critical path until upstream ships free-threaded (cp314t) wheels.[-]Replace Pandas with Polars[/-][+]Revert: Replace Pandas with Polars[/+]