Add basic capi error support#7787
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughInitializes and exposes C-API exception statics and object/type helpers, adds FFI result conversions and a with_vm helper, makes VM exception APIs public, registers the main interpreter via ChangesC‑API Exception & Object Bridge
Sequence DiagramsequenceDiagram
participant C as C caller
participant CApi as rustpython-capi
participant VM as VirtualMachine
participant Exc as ExceptionStack
C->>CApi: PyErr_SetObject(exc_type, value)
CApi->>VM: with_vm -> &VirtualMachine
VM->>VM: normalize_exception(type, value) -> PyBaseExceptionRef
VM-->>CApi: PyBaseExceptionRef
CApi->>Exc: vm.set_exception(Some(exc))
C->>CApi: PyErr_Occurred()
CApi->>VM: with_vm -> vm.current_exception()
VM-->>CApi: Option<PyBaseExceptionRef>
CApi-->>C: *mut PyObject or NULL
C->>CApi: PyErr_GetRaisedException()
CApi->>VM: with_vm -> vm.take_raised_exception()
VM-->>CApi: Option<PyBaseExceptionRef>
CApi-->>C: *mut PyObject or NULL
C->>CApi: PyErr_PrintEx()
CApi->>VM: with_vm -> vm.take_raised_exception()
VM->>VM: print_exception(exc)
VM-->>CApi: done
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly Related PRs
Suggested Reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/capi/src/pyerrors.rs`:
- Around line 138-145: Replace all use of expect!/panic!/assert! in the FFI
entrypoints so they fail gracefully through the Python/C-API: validate C string
UTF-8 in PyErr_SetString and on error create and set a Python Unicode/ValueError
via the VM and return the C-API sentinel value for that function; check for
missing exception types in PyErr_PrintEx and PyErr_WriteUnraisable and on
failure set an appropriate Python exception and return early; handle downcast
failures in PyErr_DisplayException by setting a TypeError and returning the
function's sentinel; and in PyErr_NewException replace expect/panic/assert with
input validation that sets a Python exception (TypeError/ValueError) via the VM
and returns NULL or the proper error sentinel. Locate and update the functions
named PyErr_SetString, PyErr_PrintEx, PyErr_DisplayException,
PyErr_WriteUnraisable, and PyErr_NewException to perform these checks and use
the VM to set exception state instead of aborting.
- Around line 99-105: PyErr_Occurred is returning the exception instance instead
of the exception type; change the mapping inside PyErr_Occurred to return the
exception's type object pointer rather than exc.as_object().as_raw(). Locate the
closure in the PyErr_Occurred function that calls vm.current_exception() and
replace the mapping to use the exception's class/type accessor (e.g.,
exc.class().as_object().as_raw() or the equivalent method that returns the
exception type object) while keeping the unwrap_or_default fallback.
- Around line 118-123: PyErr_SetRaisedException currently invokes undefined
behavior by calling NonNull::new_unchecked(exc) when exc can be NULL; change the
handling to use NonNull::new(exc) and branch on the Option: if NULL then clear
the VM error indicator (return Ok or equivalent) otherwise construct the
PyObjectRef via PyObjectRef::from_raw(...) and downcast_unchecked as before and
return Err(exception). Ensure you update the closure passed to with_vm to handle
the None case (clear/ok) and the Some(nonnull) case to build and return the
Err(exception).
In `@crates/capi/src/util.rs`:
- Around line 79-84: The impl of FfiResult for usize currently does an unchecked
cast in into_output, which can silently overflow; update into_output on the
usize impl to perform a checked conversion to isize (compare self to isize::MAX
or use try_from) and return ERR_VALUE if it would overflow while also setting a
Python exception on the provided VirtualMachine (use the VM API to raise an
OverflowError or ValueError), otherwise return the safely-cast isize; keep
ERR_VALUE as -1 and only use it when an exception was set.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: a85d532a-6303-4655-944d-c31fdb0ed9f0
📒 Files selected for processing (8)
crates/capi/src/lib.rscrates/capi/src/pyerrors.rscrates/capi/src/pylifecycle.rscrates/capi/src/pystate.rscrates/capi/src/util.rscrates/vm/src/builtins/type.rscrates/vm/src/vm/mod.rssrc/lib.rs
fbb09f2 to
2562f9d
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (1)
crates/capi/src/util.rs (1)
79-85:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winAvoid panicking conversion in the FFI output path (
usize→isize).Line [82]-Line [85] uses
expect, which can panic instead of setting a Python exception and returning the sentinel. In this bridge, conversion overflow should map toERR_VALUEand set VM exception.Proposed fix
impl FfiResult<isize> for usize { const ERR_VALUE: isize = -1; - fn into_output(self, _vm: &VirtualMachine) -> isize { - self.try_into() - .expect("Output value is too large to fit into target type") + fn into_output(self, vm: &VirtualMachine) -> isize { + match isize::try_from(self) { + Ok(value) => value, + Err(_) => { + vm.set_exception(Some( + vm.new_overflow_error("value does not fit in Py_ssize_t".to_owned()), + )); + Self::ERR_VALUE + } + } } }As per coding guidelines, "Follow Rust best practices for error handling and memory management."
#!/bin/bash set -euo pipefail # Verify panic-prone conversion in this impl and inspect neighboring error-mapping patterns. rg -n --type=rust -C3 'impl\s+FfiResult<isize>\s+for\s+usize|expect\(|new_overflow_error\(|set_exception\(Some\(' crates/capi/src/util.rs # Verify the overflow-error constructor naming used elsewhere in RustPython. rg -n --type=rust -C2 '\bnew_overflow_error\s*\('🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/capi/src/util.rs` around lines 79 - 85, The impl FfiResult<isize> for usize currently panics in into_output via expect; change it to handle overflow by attempting self.try_into() and, on Err, call the VM overflow-error path and return the ERR_VALUE sentinel instead of panicking. Concretely, update into_output to match on self.try_into(): on Ok(v) return v, on Err(_) create/obtain an overflow exception via VirtualMachine (e.g. new_overflow_error or the project's overflow constructor) and set it on the vm (vm.set_exception(...) or the project's equivalent), then return Self::ERR_VALUE; keep the FfiResult trait, into_output function name, ERR_VALUE constant and VirtualMachine reference to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@crates/capi/src/util.rs`:
- Around line 79-85: The impl FfiResult<isize> for usize currently panics in
into_output via expect; change it to handle overflow by attempting
self.try_into() and, on Err, call the VM overflow-error path and return the
ERR_VALUE sentinel instead of panicking. Concretely, update into_output to match
on self.try_into(): on Ok(v) return v, on Err(_) create/obtain an overflow
exception via VirtualMachine (e.g. new_overflow_error or the project's overflow
constructor) and set it on the vm (vm.set_exception(...) or the project's
equivalent), then return Self::ERR_VALUE; keep the FfiResult trait, into_output
function name, ERR_VALUE constant and VirtualMachine reference to locate the
change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: fc955e62-f835-48d4-974d-f7bec9602b20
📒 Files selected for processing (8)
crates/capi/src/lib.rscrates/capi/src/pyerrors.rscrates/capi/src/pylifecycle.rscrates/capi/src/pystate.rscrates/capi/src/util.rscrates/vm/src/builtins/type.rscrates/vm/src/vm/mod.rssrc/lib.rs
🚧 Files skipped from review as they are similar to previous changes (5)
- crates/capi/src/pystate.rs
- crates/capi/src/pylifecycle.rs
- crates/capi/src/lib.rs
- crates/vm/src/vm/mod.rs
- crates/capi/src/pyerrors.rs
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/capi/src/object.rs`:
- Around line 70-80: The extern "C" function Py_GetConstantBorrowed currently
panics on invalid constant_id which will abort the process; update the match
default arm to return a NULL pointer instead of calling panic!. Inside the
with_vm closure (using vm and ctx), replace the _ => panic!(...) branch with a
branch that returns std::ptr::null_mut() (a *mut PyObject) so the C API gets
NULL for invalid args; ensure the match arms all yield the same *mut PyObject
type and remove the panic invocation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 32705d29-6198-436c-8c01-f12f08d4c9c6
📒 Files selected for processing (2)
crates/capi/src/lib.rscrates/capi/src/object.rs
6134442 to
cfc5316
Compare
7714ba4 to
ce9322b
Compare
ce9322b to
9e20cf7
Compare
This reverts commit bccd38e.
3cc31c2 to
d34ef6f
Compare
|
@coderabbitai resume |
✅ Actions performedReviews resumed. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yaml:
- Around line 119-123: The `if` condition on the "run c-api tests" step
incorrectly checks for 'windows-2025'; update the condition `if: runner.os !=
'windows-2025'` to use the actual runner OS value `if: runner.os != 'Windows'`
so the step skips on Windows runners (match the existing pattern used elsewhere
in the file).
In `@crates/capi/src/object.rs`:
- Around line 39-66: The code casts ty.slots.flags.bits() (u64) to c_ulong which
is 32-bit on MSVC, causing truncation of internal flags (e.g.
HAS_DICT/HAS_WEAKREF at bits 40/41); fix by masking and asserting: compute let
raw = ty.slots.flags.bits(); debug_assert_eq!(raw & !0xffff_ffff, 0, "unexpected
high bits set (HAS_DICT/HAS_WEAKREF)"); let flags = (raw & 0xffff_ffff) as
c_ulong; return that—this ensures consistent behavior across platforms and makes
unintended high-bit use detectable; update the code around where flags is built
(references: ty.slots.flags.bits(), PY_TPFLAGS_* constants,
HAS_DICT/HAS_WEAKREF).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: e60c2948-0736-4795-a30b-29055716fa2c
📒 Files selected for processing (3)
.github/workflows/ci.yamlcrates/capi/src/object.rscrates/capi/src/pystate.rs
d34ef6f to
8432c17
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yaml:
- Line 115: The CI workflow currently hardcodes "--exclude rustpython-capi" into
the cargo test command instead of adding it to the shared env variable, causing
clippy to still include that crate on Windows; update the WORKSPACE_EXCLUDES
environment variable to include "rustpython-capi" (so it is respected by all
jobs) and remove the inline "--exclude rustpython-capi" from the cargo test
invocation (the line containing "run: cargo test --workspace --exclude
rustpython-capi ...") so tests and the clippy job both consume ${{
env.WORKSPACE_EXCLUDES }} consistently; also verify the clippy command that uses
${{ env.WORKSPACE_EXCLUDES }} remains unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: ecdbda11-4d2e-401d-99da-81702c01e3ad
📒 Files selected for processing (1)
.github/workflows/ci.yaml
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
crates/capi/src/pyerrors.rs (1)
195-239:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift
PyErr_NewException*still rejects or drops supported CPython inputs.
PyErr_NewException()is documented to accept an optionaldictand either a single base class or a tuple of base classes, andPyErr_NewExceptionWithDoc()should use a non-NULLdocas the new class docstring. In the current code, a non-nulldicthitsassert!,_docis ignored entirely, and malformedname/baseinputs still go throughexpect!/panic!()instead of failing via the normal C-API error path. That makes this exported surface observably incompatible for extensions that rely on these arguments. (docs.python.org)As per coding guidelines, "Follow Rust best practices for error handling and memory management".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/capi/src/pyerrors.rs` around lines 195 - 239, PyErr_NewException and PyErr_NewExceptionWithDoc currently panic/expect on bad inputs, ignore the _doc arg, and assert on a non-null dict; change them to validate inputs and fail via the C-API error path (return null pointer after setting a Python exception) instead of panicking. Specifically: in PyErr_NewException, replace the CStr::to_str()/rsplit_once and base parsing expect/panic paths with graceful checks that set an appropriate vm exception and return std::ptr::null_mut() on error; accept a non-null dict by using it to populate the new type’s dict (instead of assert!), and when base is NULL treat it as using the default base per CPython semantics or validate tuple contents and error out cleanly. In PyErr_NewExceptionWithDoc, forward name/base/dict but use the non-NULL _doc to set the new type’s __doc__ (convert C string safely and set an error/return null on failure) before returning the new exception type created by vm.ctx.new_exception_type. Ensure all error paths use the VM/C-API error-setting mechanism rather than panic!/expect! and return null pointers on failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/capi/src/pystate.rs`:
- Around line 50-51: The current stubs PyEval_SaveThread and
PyEval_RestoreThread must be implemented to bridge RustPython's threading API to
the C-API: change PyEval_SaveThread (signature returning *mut PyThreadState) to
call the equivalent of releasing the GIL (use
allow_threads()/release_current_thread() semantics) and return a pointer-sized
token representing the saved thread state (PyThreadState) that can later be
used; implement PyEval_RestoreThread to accept that token and re-acquire/restore
the thread state by calling the attach/re-acquire logic
(attach_current_thread()/acquire GIL) using the passed PyThreadState token.
Ensure both functions reference the exact symbols PyEval_SaveThread,
PyEval_RestoreThread and type PyThreadState so C extensions using
Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS get correct GIL release/restore
behavior.
---
Duplicate comments:
In `@crates/capi/src/pyerrors.rs`:
- Around line 195-239: PyErr_NewException and PyErr_NewExceptionWithDoc
currently panic/expect on bad inputs, ignore the _doc arg, and assert on a
non-null dict; change them to validate inputs and fail via the C-API error path
(return null pointer after setting a Python exception) instead of panicking.
Specifically: in PyErr_NewException, replace the CStr::to_str()/rsplit_once and
base parsing expect/panic paths with graceful checks that set an appropriate vm
exception and return std::ptr::null_mut() on error; accept a non-null dict by
using it to populate the new type’s dict (instead of assert!), and when base is
NULL treat it as using the default base per CPython semantics or validate tuple
contents and error out cleanly. In PyErr_NewExceptionWithDoc, forward
name/base/dict but use the non-NULL _doc to set the new type’s __doc__ (convert
C string safely and set an error/return null on failure) before returning the
new exception type created by vm.ctx.new_exception_type. Ensure all error paths
use the VM/C-API error-setting mechanism rather than panic!/expect! and return
null pointers on failure.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 543a9385-81ea-4e71-b76b-eb98502f92bc
📒 Files selected for processing (2)
crates/capi/src/pyerrors.rscrates/capi/src/pystate.rs
|
This is ready for final review. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/capi/src/object.rs (1)
14-21:Py_IS_TYPE: type conversion is handled correctly;with_vmnecessity remains unresolved.The closure's
boolreturn value is converted toc_intvia theFfiResult<c_int> for boolimplementation (util.rs:104–110), which castsbooltoc_intdirectly. This compiles successfully.However,
Py_TYPE(line 10–12) performs similar class-pointer access without awith_vmwrapper, whilePy_IS_TYPEwraps theis()comparison insidewith_vm. This may be intentional ifis()requires VM context for type comparison; if not, thewith_vmwrapper could be removed for consistency and simplicity.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/capi/src/object.rs` around lines 14 - 21, The Py_IS_TYPE function unnecessarily wraps the class pointer comparison in with_vm while Py_TYPE accesses op.class() directly; if obj.class().is(ty) does not require VM state, remove the with_vm wrapper and mirror Py_TYPE’s pattern (use unsafe deref of op and ty and call obj.class().is(ty) directly) so the behavior and calling convention are consistent; if the is() path actually needs VM context, instead keep with_vm but add a brief inline comment on why Py_IS_TYPE requires VM state while Py_TYPE does not.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@crates/capi/src/object.rs`:
- Around line 14-21: The Py_IS_TYPE function unnecessarily wraps the class
pointer comparison in with_vm while Py_TYPE accesses op.class() directly; if
obj.class().is(ty) does not require VM state, remove the with_vm wrapper and
mirror Py_TYPE’s pattern (use unsafe deref of op and ty and call
obj.class().is(ty) directly) so the behavior and calling convention are
consistent; if the is() path actually needs VM context, instead keep with_vm but
add a brief inline comment on why Py_IS_TYPE requires VM state while Py_TYPE
does not.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 4f54f4da-4ccd-4a88-99a3-36ccadfa4635
📒 Files selected for processing (1)
crates/capi/src/object.rs
youknowone
left a comment
There was a problem hiding this comment.
I will try to properly review this soon. Sorry for delay
| - name: run c-api tests | ||
| working-directory: crates/capi | ||
| run: cargo test | ||
| if: runner.os != 'Windows' # Requires pyo3 0.29+ on Windows |
There was a problem hiding this comment.
anything wrong about this? we can pick proper pyo3 version for windows
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Cargo.toml`:
- Line 106: The Cargo.toml currently references pyo3 and pyo3-ffi as unpinned
git dependencies which breaks reproducible builds; update both pyo3 and pyo3-ffi
entries in Cargo.toml to pin them to the known-good commit
901b032bfb8ec8eb05b39be3b98ef880427cf15a by adding the git URL and rev (or tag)
fields for each dependency so Cargo always fetches that commit (ensure you
update both the pyo3 and pyo3-ffi dependency entries).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: e8e653be-0610-4f3a-a46e-c6deecd09545
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (2)
.github/workflows/ci.yamlCargo.toml
🚧 Files skipped from review as they are similar to previous changes (1)
- .github/workflows/ci.yaml
|
|
||
| [patch.crates-io] | ||
| parking_lot_core = { git = "https://github.com/youknowone/parking_lot", branch = "rustpython" } | ||
| pyo3-ffi = { git = "https://github.com/PyO3/pyo3" } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Get the current HEAD commit of the PyO3 repository
echo "Fetching latest PyO3 commit from main branch..."
git ls-remote https://github.com/PyO3/pyo3.git HEAD | awk '{print $1}'Repository: RustPython/RustPython
Length of output: 154
🌐 Web query:
PyO3 Windows test fixes May 2026
💡 Result:
As of 2026-05-09, the most concrete PyO3 “Windows test fixes” that landed in 2026 are: 1) Windows linking strategy fix used by PyO3 tests: switch to raw-dylib (eliminate import libs), plus an x86-only workaround PyO3 introduced a change to use #[link(kind = "raw-dylib")] for Windows linking (via the pyo3-ffi layer), removing the need for Windows import libraries in the general case [1]. The PR also documents an x86-specific workaround: without an extra leading underscore for certain CPython private _Py* symbols, rustc generates wrong imports and the test binaries fail to start with STATUS_ENTRYPOINT_NOT_FOUND [1]. 2) Follow-on ecosystem work so tooling understands the “Windows import lib support” change (reducing CI/test failures) Maturin added logic to treat PyO3 0.29.0+ as having Windows import-lib support using the raw-dylib behavior, and notes that the upcoming 0.29.0 release will use raw-dylib linking on Windows, eliminating the need for the generate-import-lib feature [2]. This directly targets Windows build/test failures caused by mismatched expectations around import-library generation [2]. 3) CI coverage addition for Windows debug builds (stopping regressions from slipping through) PyO3 also added CI to test the minimum supported debug build of Python (including Windows debug builds in the CI matrix), by pinning the standalone debug binaries it downloads [3]. This is a test-related fix in the sense of preventing Windows debug ABI/linking issues from reappearing unnoticed [3]. Additional related Windows test/CI issue you may care about (but not specifically “May 2026 fixes”) A PyO3 issue discusses “pytests crash on windows free-threaded interpreter” involving module init failures and/or mismatched libpython naming on Windows [4]. The thread indicates Windows-specific debugging around which DLLs are loaded and conditional checks around sysconfig flags [4]. If you tell me what you mean by “May 2026” (e.g., issues opened in May 2026 vs. changes released in May 2026 vs. your local failing test log), I can narrow this to only the May-specific items; with the evidence I found, the strongest 2026 Windows test reliability fixes are centered on the raw-dylib/import-lib change and CI debug coverage rather than a single May-only patch.
Citations:
- 1: pyo3-ffi: use raw-dylib for Windows linking, eliminating import libraries PyO3/pyo3#5866
- 2: PyO3/maturin@5c921b7
- 3: ci: Add test for minimum supported debug build of Python PyO3/pyo3#5852
- 4: pytests crash on windows free-threaded interpreter PyO3/pyo3#4685
🏁 Script executed:
# Check the Cargo.toml file to understand the full context
head -300 Cargo.toml | tail -200Repository: RustPython/RustPython
Length of output: 7007
🏁 Script executed:
# Look for pyo3 related entries in Cargo.toml to understand the full dependency structure
rg -n "pyo3" Cargo.toml -A 2 -B 1Repository: RustPython/RustPython
Length of output: 688
🌐 Web query:
PyO3 0.29.0 release Windows raw-dylib linking 2026
💡 Result:
PyO3 0.29.0 release status (as of 2026-05-09) I could not find a published PyO3 v0.29.0 release tag/release notes in the official PyO3 GitHub releases list; the changelog/release streams I found show up through 0.28.3 in 2026. [1][2][3] Windows “raw-dylib” linking for PyO3 0.29.0 Even though a v0.29.0 release was not found, upstream development clearly indicates that the upcoming PyO3 0.29.0 would switch Windows linking to Rust’s #[link(kind = "raw-dylib")] approach (“raw-dylib linking”), eliminating the need to generate Windows Python import libraries (.lib) via the previous generate-import-lib/python3-dll-a path. 1) Core change in pyo3-ffi (Windows raw-dylib) PyO3 PR #5866 (“pyo3-ffi: use raw-dylib for Windows linking, eliminating import libraries”) describes replacing extern blocks with a macro that adds #[link(kind = "raw-dylib")] on Windows targets, using a build-script emitted pyo3_dll cfg to select the target DLL name. [4] It also states that the raw-dylib feature was stabilized in Rust 1.71 for Windows targets and that PyO3’s MSRV is 1.83, so this is compatible. [4] 2) Related “import lib support” detection in maturin for pyo3 0.29.0+ A maturin commit (#3145, Apr 18 2026) explicitly says: “Upcoming pyo3 0.29.0 release will use raw-dylib linking on Windows, eliminating the need for the generate-import-lib feature,” and adjusts logic to treat pyo3 0.29.0+ as always having Windows import lib support via raw-dylib (so import library generation is no longer needed). [5] 3) PyO3 user guide confirmation that Windows uses raw-dylib (cross-compiling case) PyO3’s building/distribution docs state: “On Windows, this variable is not needed because PyO3 uses raw-dylib linking.” [6] What to take away for “raw-dylib linking Windows” - The raw-dylib linking strategy is documented in PyO3’s Windows cross-compilation guidance. [6] - Upstream implementation work to eliminate import library generation is documented in PyO3 PR #5866. [4] - Downstream build tooling (maturin) explicitly expects PyO3 0.29.0+ to use raw-dylib on Windows and therefore removes the need for generate-import-lib. [5] If you meant something specific about “release Windows raw-dylib linking 2026” (e.g., a particular date of the v0.29.0 tag or exact changelog entry), tell me whether you want the expected release date/ETA from planning sources or whether you only care about the Windows linking behavior; I can then narrow the search accordingly.
Citations:
- 1: https://github.com/pyo3/pyo3/releases
- 2: https://pyo3.rs/main/changelog
- 3: https://github.com/PyO3/pyo3/releases/tag/v0.28.3
- 4: pyo3-ffi: use raw-dylib for Windows linking, eliminating import libraries PyO3/pyo3#5866
- 5: PyO3/maturin@5c921b7
- 6: https://pyo3.rs/main/building-and-distribution
Pin git dependencies to specific commits for reproducible builds.
Both pyo3 and pyo3-ffi are using unpinned git dependencies pointing to the default branch HEAD. This breaks build reproducibility because different build times will pull different versions of the code, and upstream changes could introduce breaking changes or instability.
The project already follows this best practice elsewhere (e.g., lines 172-175 show Ruff dependencies pinned to commit hashes "to ensure reproducible builds").
The Windows fix you need (raw-dylib linking for Windows support) is present in the current PyO3 main branch at commit 901b032bfb8ec8eb05b39be3b98ef880427cf15a. Pin both dependencies to this commit:
Recommended fix
[patch.crates-io]
parking_lot_core = { git = "https://github.com/youknowone/parking_lot", branch = "rustpython" }
-pyo3-ffi = { git = "https://github.com/PyO3/pyo3" }
+pyo3-ffi = { git = "https://github.com/PyO3/pyo3", rev = "901b032bfb8ec8eb05b39be3b98ef880427cf15a" } pymath = { version = "0.2.0", features = ["mul_add", "malachite-bigint", "complex"] }
-pyo3 = { git = "https://github.com/PyO3/pyo3" }
+pyo3 = { git = "https://github.com/PyO3/pyo3", rev = "901b032bfb8ec8eb05b39be3b98ef880427cf15a" }Also applies to: 260-260
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Cargo.toml` at line 106, The Cargo.toml currently references pyo3 and
pyo3-ffi as unpinned git dependencies which breaks reproducible builds; update
both pyo3 and pyo3-ffi entries in Cargo.toml to pin them to the known-good
commit 901b032bfb8ec8eb05b39be3b98ef880427cf15a by adding the git URL and rev
(or tag) fields for each dependency so Cargo always fetches that commit (ensure
you update both the pyo3 and pyo3-ffi dependency entries).
This is the next step in adding c-api support. It also contains utilities to handle errors for implementing other c-api functions.
Summary by CodeRabbit
New Features
Refactor
Chores
Tests / CI