Skip to content

Run pants under free threaded python - #23477

Merged
tobni merged 11 commits into
pantsbuild:mainfrom
tobni:free-threaded-python
Aug 9, 2026
Merged

Run pants under free threaded python#23477
tobni merged 11 commits into
pantsbuild:mainfrom
tobni:free-threaded-python

Conversation

@tobni

@tobni tobni commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Switches Pants' own runtime to free-threaded CPython and makes the engine and
Pants' Python code thread-safe without the GIL.

This has been validated as running without error in my companies CI, and nets great speedups.

Most important callouts:

  • Keep a persistent per-thread PyThreadState in the executor. Without
    this, every Python::attach cycle tears down the thread state, and on 3.14t
    that abandons the thread's mimalloc heaps. Created per tokio thread, torn down in on_thread_stop (blocking
    threads are recycled, so the state must not leak per thread creation).
  • Makes run-scoped internals thread-safe: rule_visitor, exception_sink, logging, build_root, run_tracker.

I used #23437 as a launching point.

I have on speculation set this as shipping in 2.34.x, as I believe 2.33 is too far along the dev-cycles. But I also do not see any reason to delay further.

Blocks pantsbuild/scie-pants#541

Edit:
This branch now serves as proof of green CI of the accumulation of changes, and will be rebased as its parts are accepted incrementally:

Edit2:

Now that all pre-requisites are in, this PR contains CI/CD changes and the new PANTS_FREE_THREADED option.
Current proposal for consumer UX is

Consumer pants.toml#[GLOBAL].pants_free_threaded Resolves to Bootstrap warning
scie-pants - not updated absent / false cp314 (GIL 3.14) -
scie-pants - not updated true cp314 (GIL 3.14) "update scie-pants". Old launcher can't honor the field
scie-pants - updated absent (default) cp314t (free-threaded) -
scie-pants - updated false cp314 (GIL 3.14) - (escape hatch)
scie-pants - updated true cp314t (free-threaded) -
Fat scie - …-cp314 true GIL 3.14 "no effect on a fat scie; download the binary you want"
Fat scie - …-cp314t false free-threaded "no effect on a fat scie; download the binary you want"

This PR is coupled to pantsbuild/scie-pants#541.

@tobni tobni mentioned this pull request Jul 2, 2026
@tobni
tobni force-pushed the free-threaded-python branch 2 times, most recently from c68208d to d3a9c73 Compare July 2, 2026 15:30
@cburroughs

Copy link
Copy Markdown
Contributor

(Some adjacent or related issues #22790 #22946 #15840 #21518 so I don't need to search for them again.)

@tobni As I understand it there isn't a stable ABI for free threaded until 3.15. Having dug in to do this work, do you have a sense of how much of a problem or risk that is (or is not)?

@tobni

tobni commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@cburroughs Risk is close to none afaik, it is mostly a pyo3 compat (meaning we couldnt build it) issue. If we are allowed to assume pants is ran as a scie we defined, the internal 3rd party lockfile does the rest. I think that is ok, building a pants dist and targeting a different python would be highly special usecase.

If a plugin uses native deps in rule code that could bite them iff there is no 3.14t wheels for them.

@tobni
tobni force-pushed the free-threaded-python branch 2 times, most recently from 6a60274 to b03a612 Compare July 2, 2026 17:42
@sureshjoshi
sureshjoshi requested review from benjyw, cburroughs, ndellosa95, sureshjoshi and tdyas and removed request for benjyw July 2, 2026 17:56
@sureshjoshi

Copy link
Copy Markdown
Member

Pretty substantial (and consequential) change - so I tacked on a bunch of reviewers

Comment thread src/python/pants/BUILD
@tobni

tobni commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Once #23482 is in this will be rebased and the pex binary will again be stripped.

Edit: Done.

@tobni tobni mentioned this pull request Jul 3, 2026
@tdyas

tdyas commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

General review comments:

  1. This PR should probably be split into distinct components to make review easier. For example, the LockMap change and the related memoization changes can be a standalone PR.

  2. Is it feasible to make the free-threaded Python be a runtime choice? I'd rather we prefer it be an opt-in experiment initially. If it can't be done based on a runtime option, maybe let scie-pants know how to configure it and download a separate Pants artifact with free-threaded support?

@tobni

tobni commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Is it feasible to make the free-threaded Python be a runtime choice? I'd rather we prefer it be an opt-in experiment initially. If it can't be done based on a runtime option, maybe let scie-pants know how to configure it and download a separate Pants artifact with free-threaded support?

We could vendor release versions with t-suffixes to make it target free-threaded? I'm doubtful of the value to be honest, but that'd be simple enough implementation wise (but requires CI/CD work).

I'd prefer opt-out though. I dont want to treat this as a scary change when it isnt, but I will also not claim 0 impact across all plugin code.

For the first comment: Yes, easily split. Each commit ~stands on its own already so I can do that branch-ectomy with ~0 effort.

@tdyas

tdyas commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

We could vendor release versions with t-suffixes to make it target free-threaded? I'm doubtful of the value to be honest, but that'd be simple enough implementation wise (but requires CI/CD work).

So like everything in software engineering, my view is "it depends." My preference here is that we not introduce a one way version gate on users when there is no one dedicated to fixing issues for them in a timely manner. I'd rather avoid users being potentially stuck on 2.32 or lower versions with no way to consume all of the other fixes Pants has in 2.33 unrelated to free-threading.

So how much more complex would it be to release a free-threading version of Pants alongside the GIL version? The main artifact is the engine's shared library (native_engine.so), can we just build it twice? (once with 3.14 GIL and once with 3.14 free-threaded)?

My view really depends on just how much additional work the CI/CD & release changes would be.

I'd prefer opt-out though. I dont want to treat this as a scary change when it isnt, but I will also not claim 0 impact across all plugin code.

Opt-out is fine. The concern for me is having the ability to control adoption of the change with some sort of flag versus forcing users to stay on a prior versions because there is no escape hatch from this change. I submit that Incremental adoption with an escape hatch makes for a better user experience.

@tobni

tobni commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

So like everything in software engineering, my view is "it depends." My preference here is that we not introduce a one way version gate on users when there is no one dedicated to fixing issues for them in a timely manner. I'd rather avoid users being potentially stuck on 2.32 or lower versions with no way to consume all of the other fixes Pants has in 2.33 unrelated to free-threading.

You used specific version numbers so I'll just clarify that I do not think this is a 2.33.x change but rather a 2.34.x feature. The point is of course still valid. I'm not sure I agree that this is a strong case for pants and free threading though, we have a patch-version series for fixes and we vendor a binary. We can choose to only release free threading and address eventual bugs. Because it will be bugs, users being "stuck" will be on some flaky CI-runs surfacing races we missed, most likely. Unless of course there is ABI-incompatible user plugins out there.

So how much more complex would it be to release a free-threading version of Pants alongside the GIL version? The main artifact is the engine's shared library (native_engine.so), can we just build it twice? (once with 3.14 GIL and once with 3.14 free-threaded)?

I dont know exactly, the main effort is figuring that out, and is the counterweight in terms of value I was refering to.

@tobni

tobni commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Now I know some more - It'd have to be a separate pants.toml field to not mess with PEP440 compat, but otherwise it goes:

  1. One release, two asset sets. The GitHub release has to publish both scies per platform eg: pants.2.34.0-linux-x86_64.scie and a free threaded sibling whose naming is TBD.
  2. Selection is a scie-pants config knob. scie-pants reads from pants.toml. scie-pants maps the pants.toml field to the asset-name suffix and fetches.

Essentially a question of if we're okay to publish 2x scies.

@tobni
tobni force-pushed the free-threaded-python branch 2 times, most recently from afe6389 to 45d78c2 Compare July 3, 2026 23:03
@tobni

tobni commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

General review comments:

  1. This PR should probably be split into distinct components to make review easier. For example, the LockMap change and the related memoization changes can be a standalone PR.
  2. Is it feasible to make the free-threaded Python be a runtime choice? I'd rather we prefer it be an opt-in experiment initially. If it can't be done based on a runtime option, maybe let scie-pants know how to configure it and download a separate Pants artifact with free-threaded support?

@tdyas 1 is now done. See the list in the pull request description.

tobni added a commit that referenced this pull request Jul 10, 2026
Shaves of about 0.1s startup time. The second commit is
1. Preparatory work for when source plugins are imported in parallell,
regular sequential import leaves perf on the table
2. If the python backend is not used, in which case importing pex-rules
is wasted work. Not sure if those users exists, though.

I intend to introduce paralllel backend imports after
#23477.

I do not think this conflicts in spirit with
#21991. It does conflict in
direction with a comment here:
#11568, however I find
#10360 to have
resolved/obsoleted the motivation for the bootstrap scheduler there as
well.
@tobni
tobni force-pushed the free-threaded-python branch 4 times, most recently from 1c1496a to cb73149 Compare July 13, 2026 16:01
@tobni
tobni force-pushed the free-threaded-python branch 4 times, most recently from 080985a to f330557 Compare July 26, 2026 13:32
Comment thread src/rust/task_executor/src/lib.rs Outdated
Comment thread src/python/pants_release/generate_github_workflows.py Outdated
@tobni
tobni force-pushed the free-threaded-python branch from 2ae7144 to 5daeffb Compare August 4, 2026 18:02

@cburroughs cburroughs left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for continuing to push this. Approving assuming you can figure out the last bit of Actions Workflow changes needed (check current fails.) Let me know if that turns into a can of worms.

tobni added 10 commits August 8, 2026 13:45
Like pants_version, the option is consumed by the scie-pants launcher,
which picks between the cp314t and cp314 distributions of a release.
PANTS_FREE_THREADED overrides the config value.
The wheel build jobs are parametrized over the two interpreter abis.
Wheel tags and PEX asset names already derive from sys.abiflags.
Bounds memory growth observed by test_pantsd_memory_usage that otherwise is only reclaimed after >10s
Use output_paths for neater artifact names
Clarified commenting
@tobni
tobni force-pushed the free-threaded-python branch from d702c01 to 75326a5 Compare August 8, 2026 11:48
@tobni

tobni commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for continuing to push this. Approving assuming you can figure out the last bit of Actions Workflow changes needed (check current fails.) Let me know if that turns into a can of worms.

It's just a stale pex version pin, it'll self-sync once I get this onto main.

@benjyw Your blessing is the confidence I need to merge this. PTAL at the table in the pull request description about UX-cases.



def pinned_pex() -> tuple[str, str]:
# visibility forbids `pants_release` depending on `pants.backend`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't though? pants_release morally depends on all of pants... Maybe just fix that?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I assumed there was design intent behind the visibility rule.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`pants_release` may now depend on backend sources.
@tobni
tobni requested a review from benjyw August 9, 2026 16:33

@benjyw benjyw left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!!!

@tobni
tobni merged commit dc51373 into pantsbuild:main Aug 9, 2026
28 checks passed
@tobni
tobni deleted the free-threaded-python branch August 9, 2026 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants