Skip to content

Build Python 3.14 free-threaded wheels#36

Merged
winstxnhdw merged 4 commits into
litestar-org:mainfrom
edgarrmondragon:pyo3-0.27
Feb 15, 2026
Merged

Build Python 3.14 free-threaded wheels#36
winstxnhdw merged 4 commits into
litestar-org:mainfrom
edgarrmondragon:pyo3-0.27

Conversation

@edgarrmondragon

@edgarrmondragon edgarrmondragon commented Jan 4, 2026

Copy link
Copy Markdown
Contributor

Description

Build Python 3.14 free-threaded wheels.

From Quansight Labs' Free-Threading Guide:

The ultimate problem is that CPython does not yet offer a stable ABI on the free-threaded build. See this CPython issue for more details. This means it's not yet possible to build an extension that any CPython version can import and instead libraries have to distribute one wheel for each version of free-threaded CPython. A future version of CPython will likely support a new version of the stable ABI that works correctly with both the GIL-enabled and free-threaded builds.

I also took the opportunity to bump PyO3 to 0.27 and migrate the project to the latest API.

Closes

NA

Summary by Sourcery

Add support for Python 3.14 free-threaded builds and update the Rust/PyO3 bindings accordingly.

New Features:

  • Support building and publishing wheels for Python 3.14 free-threaded (3.14t).

Enhancements:

  • Update PyO3 and pythonize dependencies to their latest APIs and adjust module bindings for GIL-free compatibility.
  • Raise the minimum supported Python version to 3.8 and expand the CI test matrix to cover Python 3.8–3.14 and 3.14t.
  • Run pytest with parallel threads in CI to speed up test execution.

CI:

  • Extend CI workflows to test against the new Python 3.14 free-threaded interpreter variant and run tests in parallel.

Deployment:

  • Adjust the publish workflow environment to build wheels targeting both standard 3.8 and free-threaded 3.14 Python versions.

@sourcery-ai

sourcery-ai Bot commented Jan 4, 2026

Copy link
Copy Markdown

Reviewer's Guide

Updates the Rust extension to be compatible with the latest PyO3 and Python 3.14 free-threaded builds, adjusts the Python/Rust APIs accordingly, and extends CI and publishing workflows to build and test against the new interpreter variants with improved parallel test execution.

Class diagram for updated Rust PyO3 extension API

classDiagram
    class FastQueryParsersModule {
        +gil_used : bool
        +fast_query_parsers(_py : Python, m : Bound_PyModule) PyResult_unit
        +parse_query_string(qs : slice_u8, separator : char) PyResult_Vec_String_String
        +parse_url_encoded_dict(py : Python, qs : slice_u8, parse_numbers : bool) PyResult_Py_PyAny
    }

    class PyO3 {
        +version : 0_27_2
        +features : extension_module, abi3_py38
    }

    class Pythonize {
        +version : 0_27_0
    }

    FastQueryParsersModule --> PyO3 : uses
    FastQueryParsersModule --> Pythonize : uses
Loading

File-Level Changes

Change Details Files
Update Rust extension API types for PyO3 0.27 and free-threaded compatibility.
  • Change parse_url_encoded_dict return type from generic PyObject to Py to align with modern PyO3 ownership patterns.
  • Wrap the result of pythonize(...).unwrap() in .into() to convert to the new return type.
  • Annotate the pymodule with gil_used = false and update the module function signature to use Bound<'_, PyModule> as required by newer PyO3 APIs.
  • Keep existing pyfunction signatures and wrappers but ensure they compile with the upgraded PyO3 version.
src/lib.rs
Bump core Rust dependencies to current versions compatible with Python 3.14 and PyO3 0.27.
  • Update pyo3 dependency from 0.19.2 to 0.27.2 with the same extension-module and abi3-py38 features.
  • Update pythonize from 0.19.0 to 0.27.0 to match the PyO3 upgrade.
  • Refresh Cargo.lock to capture the new dependency graph.
Cargo.toml
Cargo.lock
Broaden Python support matrix and adjust packaging/publishing for Python 3.14 free-threaded builds.
  • Increase minimum supported Python version from 3.7 to 3.8 in pyproject metadata.
  • Extend GitHub Actions CI Python matrix to include 3.14t (free-threaded) alongside existing versions.
  • Update publishing workflow env.PYTHON_VERSION from a single 3.8 entry to "3.8 3.14t" to produce wheels for both standard and free-threaded interpreters.
  • Add pytest-run-parallel to dev dependencies and run pytest with --parallel-threads=10 to handle free-threaded testing load.
  • Regenerate uv.lock to reflect updated Python and dev dependency requirements.
pyproject.toml
.github/workflows/ci.yaml
.github/workflows/publish.yaml
uv.lock

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Comment thread pyproject.toml
@edgarrmondragon
edgarrmondragon marked this pull request as ready for review January 4, 2026 06:59
@edgarrmondragon edgarrmondragon changed the title Test with Python 3.14 free-threaded Build Python 3.14 free-threaded wheels Jan 4, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In parse_url_encoded_dict, the pythonize(...).unwrap() introduces a potential panic path; consider propagating the error as a PyErr (e.g., via ? or explicit mapping) instead of unwrapping.
  • The CI step uv run pytest --parallel-threads=10 hardcodes the parallelism level and may oversubscribe smaller runners; consider using an auto-detection mode or deriving the thread count from available CPUs or a workflow input.
  • In publish.yaml, PYTHON_VERSION: "3.8 3.14t" sets a single space-separated string; if this is meant to represent multiple versions for consumers of the env var, consider encoding it in a more structured way (e.g., a matrix, a comma-separated list, or separate variables) to avoid parsing ambiguity.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `parse_url_encoded_dict`, the `pythonize(...).unwrap()` introduces a potential panic path; consider propagating the error as a `PyErr` (e.g., via `?` or explicit mapping) instead of unwrapping.
- The CI step `uv run pytest --parallel-threads=10` hardcodes the parallelism level and may oversubscribe smaller runners; consider using an auto-detection mode or deriving the thread count from available CPUs or a workflow input.
- In `publish.yaml`, `PYTHON_VERSION: "3.8 3.14t"` sets a single space-separated string; if this is meant to represent multiple versions for consumers of the env var, consider encoding it in a more structured way (e.g., a matrix, a comma-separated list, or separate variables) to avoid parsing ambiguity.

## Individual Comments

### Comment 1
<location> `src/lib.rs:20-21` </location>
<code_context>
-    Ok(pythonize(py, &parse_query_string_to_json(qs, parse_numbers)).unwrap())
+fn parse_url_encoded_dict(py: Python, qs: &[u8], parse_numbers: bool) -> PyResult<Py<PyAny>> {
+    Ok(
+        pythonize(py, &parse_query_string_to_json(qs, parse_numbers))
+            .unwrap()
+            .into(),
+    )
</code_context>

<issue_to_address>
**issue (bug_risk):** Avoid panicking on pythonize() failure and propagate a proper Python exception instead.

Because this is called from Python, `unwrap()` will abort the process if `pythonize()` fails (e.g., on unexpected input). Since the function already returns `PyResult`, please propagate the error instead, for example:

```rust
pythonize(py, &parse_query_string_to_json(qs, parse_numbers))
    .map(|obj: Py<PyAny>| obj.into())
```

or:

```rust
pythonize(py, &parse_query_string_to_json(qs, parse_numbers))
    .map(Py::from)
```

This ensures failures become Python exceptions rather than panics.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/lib.rs Outdated
Signed-off-by: Edgar Ramírez Mondragón <edgarrm358@gmail.com>
Signed-off-by: Edgar Ramírez Mondragón <edgarrm358@gmail.com>
Signed-off-by: Edgar Ramírez Mondragón <edgarrm358@gmail.com>
Signed-off-by: Edgar Ramírez Mondragón <edgarrm358@gmail.com>
@edgarrmondragon

Copy link
Copy Markdown
Contributor Author

@winstxnhdw would you mind taking a look?

@winstxnhdw winstxnhdw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM.

@winstxnhdw
winstxnhdw merged commit 94b0d19 into litestar-org:main Feb 15, 2026
11 checks passed
@edgarrmondragon
edgarrmondragon deleted the pyo3-0.27 branch February 15, 2026 16:34
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.

2 participants