Skip to content

fix(python): exclude freethreaded builds from precompiled selection#8672

Merged
jdx merged 2 commits intomainfrom
fix/python-freethreaded-filter
Mar 21, 2026
Merged

fix(python): exclude freethreaded builds from precompiled selection#8672
jdx merged 2 commits intomainfrom
fix/python-freethreaded-filter

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Mar 21, 2026

Summary

  • Freethreaded Python builds (e.g. cpython-3.14.3+20260320-x86_64-unknown-linux-gnu-freethreaded-install_only_stripped.tar.gz) use lib/python3.14t/ instead of lib/python3.14/, causing "Python installation is missing a lib directory" errors during installation
  • The install_only_stripped ranking treated freethreaded and non-freethreaded builds equally, and the freethreaded variant won alphabetically via min_by_key
  • Filter out freethreaded tarballs by default unless python.precompiled_flavor is explicitly set

Test plan

  • CI e2e backend/test_pipx_deep_dependencies_slow should pass (previously failing on mise up --bump python selecting freethreaded 3.14.3)
  • Users with python.precompiled_flavor set to a freethreaded variant should still get freethreaded builds

🤖 Generated with Claude Code


Note

Medium Risk
Changes precompiled Python artifact selection and sysconfig patching path resolution, which can affect which binaries users install and whether installs succeed on newer Python versions. Risk is moderate due to platform/version variability, but logic is narrowly scoped and gated by python.precompiled_flavor.

Overview
Precompiled Python selection now filters out freethreaded artifacts by default (unless settings.python.precompiled_flavor is explicitly set), preventing freethreaded tarballs from winning tie-breaks during version ranking.

Sysconfig patching now searches lib/pythonX.Yt in addition to the existing suffix/no-suffix locations, avoiding "missing lib directory" failures for free-threaded installs that use the t lib directory convention.

Written by Cursor Bugbot for commit d29ecc7. This will update automatically on new commits. Configure here.

Freethreaded Python builds (e.g. cpython-3.14.3-freethreaded-install_only_stripped)
use a different lib directory layout (python3.14t vs python3.14), causing
"missing lib directory" errors during installation. Filter them out by default
unless the user explicitly sets python.precompiled_flavor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses an issue where freethreaded Python builds were being incorrectly selected and causing installation failures due to incompatible library paths. The changes modify the precompiled Python build selection logic to prioritize standard builds by default, ensuring a smoother installation experience for most users. Users who specifically require freethreaded builds can still opt into them via a configuration setting.

Highlights

  • Python freethreaded build path issue: Freethreaded Python builds (e.g., cpython-3.14.3+20260320-x86_64-unknown-linux-gnu-freethreaded-install_only_stripped.tar.gz) incorrectly used lib/python3.14t/ instead of lib/python3.14/, leading to "Python installation is missing a lib directory" errors.
  • Precompiled build selection logic: The previous ranking logic for precompiled Python builds treated freethreaded and non-freethreaded variants equally, with freethreaded builds sometimes being selected due to alphabetical sorting.
  • Default freethreaded build exclusion: Freethreaded tarballs are now filtered out by default during precompiled build selection, unless the python.precompiled_flavor configuration is explicitly set to a freethreaded variant.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 21, 2026

Greptile Summary

This PR fixes a regression where mise up --bump python could select freethreaded Python builds (e.g. cpython-3.14.3+20260320-...-freethreaded-install_only_stripped.tar.gz) by default, causing a "Python installation is missing a lib directory" error because freethreaded builds use lib/python3.14t/ rather than lib/python3.14/. The fix has two parts:

  • python.rs: A single-line filter is added in both fetch_precompiled_remote_versions and fetch_precompiled_for_target to exclude lines containing freethreaded unless python.precompiled_flavor is explicitly set, preserving opt-in support for freethreaded builds.
  • sysconfig/mod.rs: find_sysconfigdata is refactored to probe three lib-directory candidates in order — with suffix, without suffix, and with the t suffix — so that if a freethreaded build is explicitly installed via precompiled_flavor, sysconfig patching will still work correctly.

The approach is minimal and well-targeted. One concern: the static PRECOMPILED_CACHE in fetch_precompiled_remote_versions uses python_precompiled_platform() as its cache key, which produces the same string ({arch}-{os}) regardless of whether freethreaded entries are filtered. Users with an existing on-disk cache from before this fix will continue to see freethreaded versions until the cache TTL expires.

Confidence Score: 4/5

  • Safe to merge; one concrete but non-blocking concern around stale on-disk caches that include freethreaded entries.
  • The core filter logic is correct and directly addresses the reported bug. The sysconfig/mod.rs refactor is clean and handles the freethreaded lib-path edge case properly. The only substantive concern is that PRECOMPILED_CACHE uses the same disk-cache key before and after this fix, meaning existing users with a non-expired cache won't benefit until the TTL elapses. This is real but self-resolving and doesn't affect fresh installs. A one-line cache-key bump would fully resolve it.
  • src/plugins/core/python.rs — specifically the PRECOMPILED_CACHE key at lines 162-168.

Important Files Changed

Filename Overview
src/plugins/core/python.rs Adds `.filter(
src/sysconfig/mod.rs Refactors find_sysconfigdata to probe an ordered list of lib-directory candidates, adding python{major}.{minor}t as a third fallback to support freethreaded builds (which use lib/python3.14t/). Correctly handles both the default (non-freethreaded) path and the explicit-flavor freethreaded path without breaking existing behaviour.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[mise up --bump python] --> B[fetch_precompiled_remote_versions / fetch_precompiled_for_target]
    B --> C{precompiled_flavor set?}
    C -- Yes --> D[Pass all lines incl. freethreaded\nthrough platform filter]
    C -- No --> E[Filter out lines containing 'freethreaded']
    D --> F[Select best tarball via rank / min_by_key]
    E --> F
    F --> G[Download & extract tarball]
    G --> H[install_precompiled: call update_sysconfig]
    H --> I[find_sysconfigdata]
    I --> J{lib/pythonM.mt exists?}
    J -- Yes freethreaded --> K[Use lib/pythonM.Mt/]
    J -- No --> L{lib/pythonM.m exists?}
    L -- Yes --> M[Use lib/pythonM.m/]
    L -- No --> N[Error: MissingLib]
    K --> O[Patch _sysconfigdata_ file]
    M --> O
Loading

Comments Outside Diff (1)

  1. src/plugins/core/python.rs, line 162-169 (link)

    P1 Stale cache may contain freethreaded entries

    PRECOMPILED_CACHE is keyed with python_precompiled_platform(), which returns {arch}-{os} when precompiled_flavor is not set (e.g. x86_64-unknown-linux-gnu). This is the same key used by the old code that had no freethreaded filter. A user who upgrades mise but already has a non-expired on-disk cache populated by the old code will continue to receive freethreaded entries (because get_or_try_init_async returns the cached value and never re-runs the filtering closure) until the cache TTL elapses.

    To guarantee the fix takes effect immediately for existing users, the cache key should encode the new filtering behaviour, e.g.:

    .with_cache_key(format!("{}-v2", python_precompiled_platform()))

    or any other string that differs from what the old binary produced. Alternatively, append -no-freethreaded when flavor.is_none() to make it explicit and self-documenting.

    Fix in Claude Code

Fix All in Claude Code

Last reviewed commit: "fix(python): handle ..."

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request addresses an issue where freethreaded Python builds were being incorrectly selected due to the install_only_stripped ranking and alphabetical sorting. The changes filter out freethreaded tarballs by default unless python.precompiled_flavor is explicitly set. The code changes involve adding a filter to exclude freethreaded versions in src/plugins/core/python.rs. The review focuses on the correctness of the added filter and its impact on the intended functionality.

let versions = raw
.lines()
.filter(|v| v.contains(&platform))
.filter(|v| flavor.is_some() || !v.contains("freethreaded"))
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.

high

This filter excludes freethreaded versions unless flavor is set. It's crucial to ensure this logic doesn't inadvertently exclude desired versions when flavor is explicitly set to a non-freethreaded value. Consider adding a test case to verify this scenario.

                    .filter(|v| flavor.is_some() || !v.contains("freethreaded"))
                    // When flavor is set, we want to include freethreaded builds, so don't filter them out.

let result = raw
.lines()
.filter(|v| v.contains(&platform))
.filter(|v| flavor.is_some() || !v.contains("freethreaded"))
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.

high

Similar to the previous comment, ensure this filter doesn't exclude desired versions when flavor is explicitly set to a non-freethreaded value. A test case would be beneficial here as well.

            .filter(|v| flavor.is_some() || !v.contains("freethreaded"))
            // When flavor is set, we want to include freethreaded builds, so don't filter them out.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 21, 2026

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.9 x -- echo 25.5 ± 0.7 24.0 30.4 1.02 ± 0.04
mise x -- echo 24.9 ± 0.6 23.8 29.8 1.00

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.9 env 24.0 ± 0.7 23.1 29.3 1.00
mise env 24.1 ± 0.4 23.2 27.4 1.00 ± 0.03

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.9 hook-env 25.2 ± 0.7 23.8 28.5 1.00 ± 0.04
mise hook-env 25.1 ± 0.7 24.2 28.3 1.00

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.9 ls 23.7 ± 0.5 22.8 29.2 1.00
mise ls 24.5 ± 0.4 23.4 26.6 1.03 ± 0.03

xtasks/test/perf

Command mise-2026.3.9 mise Variance
install (cached) 153ms 153ms +0%
ls (cached) 85ms 85ms +0%
bin-paths (cached) 90ms 90ms +0%
task-ls (cached) 858ms 823ms +4%

Free-threaded Python uses `lib/python3.14t/` (note the `t` suffix).
Probe for this variant in `find_sysconfigdata` so that freethreaded
builds work when explicitly requested via `python.precompiled_flavor`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jdx jdx merged commit 04c0e7e into main Mar 21, 2026
34 of 36 checks passed
@jdx jdx deleted the fix/python-freethreaded-filter branch March 21, 2026 10:07
This was referenced Mar 21, 2026
dannysauer added a commit to dannysauer/stampbot that referenced this pull request Mar 22, 2026
mise v2026.3.10 excludes freethreaded Python builds by default,
fixing "Python installation is missing a lib directory" errors.

See: https://github.com/jdx/mise/releases/tag/v2026.3.10
Fix: jdx/mise#8672

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
dannysauer added a commit to dannysauer/stampbot that referenced this pull request Mar 22, 2026
mise v2026.3.10 excludes freethreaded Python builds by default,
fixing "Python installation is missing a lib directory" errors.

See: https://github.com/jdx/mise/releases/tag/v2026.3.10
Fix: jdx/mise#8672

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
dannysauer added a commit to dannysauer/stampbot that referenced this pull request Mar 22, 2026
mise v2026.3.10 excludes freethreaded Python builds by default,
fixing "Python installation is missing a lib directory" errors.

See: https://github.com/jdx/mise/releases/tag/v2026.3.10
Fix: jdx/mise#8672

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant