Skip to content

Add more error functions to the c-api#8248

Open
bschoenmaeckers wants to merge 2 commits into
RustPython:mainfrom
bschoenmaeckers:c-api-more-error
Open

Add more error functions to the c-api#8248
bschoenmaeckers wants to merge 2 commits into
RustPython:mainfrom
bschoenmaeckers:c-api-more-error

Conversation

@bschoenmaeckers

@bschoenmaeckers bschoenmaeckers commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Expanded native C-API for exception management, including traceback setting, handled exceptions, exc_info, and interrupt/signal helpers.
    • Added OS-error helpers to construct exception instances from errno and Windows error codes, with optional filename context.
    • Introduced C-ABI constructors and accessors for Unicode decode/encode/translate error objects.
  • Bug Fixes
    • Improved traceback handling, including normalization of “None” traceback.
    • Enhanced EINTR signal checking and stricter validation/clamping for Unicode error fields.
  • Chores
    • Refined infallible FFI error propagation when setting exceptions.

@bschoenmaeckers bschoenmaeckers changed the title C api more error Add more error functions to the c-api Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The C-API now exposes VM-backed exception state operations, platform error constructors, Unicode error accessors, traceback handling, signal utilities, and FFI error conversion.

Changes

Exception and error C-API

Layer / File(s) Summary
Exception state and FFI error plumbing
crates/capi/src/pyerrors.rs, crates/capi/src/util.rs, crates/vm/src/vm/mod.rs
Adds exception fetch/restore and handled-exception APIs, traceback and argument accessors, signal and fatal-error helpers, public error-module exports, and pointer-returning FFI error conversion.
Errno exception construction
crates/capi/src/pyerrors/errno.rs
Constructs Python exceptions from host errno values with optional filenames and EINTR signal checks.
Unicode error accessors
crates/capi/src/pyerrors/unicode.rs
Adds Unicode decode, encode, and translate error construction plus attribute accessors and mutators.
Windows error construction
crates/capi/src/pyerrors/windows.rs
Adds Windows error helpers using Win32 error codes, optional filenames, and targeted exception types or OSError.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CAPI
  participant VM
  participant Exception
  CAPI->>VM: fetch, restore, or update exception state
  VM->>Exception: normalize traceback and exception values
  Exception-->>VM: exception attributes and triple
  VM-->>CAPI: return pointers or FFI error sentinel
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: shaharnaveh, youknowone

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: additional error-related C-API functions were added.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/capi/src/pyerrors/errno.rs (1)

18-56: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reduce duplication by delegating to the object-based helper.

PyErr_SetFromErrnoWithFilename re-implements the errno read, EINTR handling, message construction, downcast, and call logic that already exists in PyErr_SetFromErrnoWithFilenameObjects. As in CPython, this can decode the filename and then delegate to PyErr_SetFromErrnoWithFilenameObject, keeping the errno/message logic in one place.

🤖 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/errno.rs` around lines 18 - 56, Refactor
PyErr_SetFromErrnoWithFilename to only decode the optional C filename, convert
it to a Python object, and delegate to PyErr_SetFromErrnoWithFilenameObject.
Remove its duplicated errno retrieval, EINTR handling, message construction,
exception downcast, and call logic so the object-based helper remains the single
implementation.
🤖 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/windows.rs`:
- Around line 69-84: In the Windows error conversion branch, avoid leaking the
decoded filename created by decode_fsdefault_and_size. Update the
Some(Ok(filename)) handling to keep the PyObjectRef alive locally and pass its
borrowed raw pointer to set_windows_error, rather than calling into_raw();
preserve the existing error and None handling.

---

Nitpick comments:
In `@crates/capi/src/pyerrors/errno.rs`:
- Around line 18-56: Refactor PyErr_SetFromErrnoWithFilename to only decode the
optional C filename, convert it to a Python object, and delegate to
PyErr_SetFromErrnoWithFilenameObject. Remove its duplicated errno retrieval,
EINTR handling, message construction, exception downcast, and call logic so the
object-based helper remains the single implementation.
🪄 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: 05c2152b-5010-4ab6-a5ca-c0afe9842760

📥 Commits

Reviewing files that changed from the base of the PR and between 9c064c1 and bebdf8e.

📒 Files selected for processing (5)
  • crates/capi/src/pyerrors.rs
  • crates/capi/src/pyerrors/errno.rs
  • crates/capi/src/pyerrors/unicode.rs
  • crates/capi/src/pyerrors/windows.rs
  • crates/capi/src/util.rs

Comment thread crates/capi/src/pyerrors/windows.rs Outdated
@bschoenmaeckers
bschoenmaeckers force-pushed the c-api-more-error branch 4 times, most recently from 8f6e3ec to 29f68a9 Compare July 13, 2026 11:44
@bschoenmaeckers

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/capi/src/pyerrors/errno.rs (1)

23-34: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Handle EINTR before decoding the filename. Match the PyErr_SetFromErrnoWithFilenameObjects ordering here: check EINTR immediately after reading errno, before decode_fsdefault_and_size(...). Otherwise a decode failure can bypass vm.check_signals() and return the wrong exception.

🤖 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/errno.rs` around lines 23 - 34, Move the errno ==
libc::EINTR handling immediately after retrieving errno in the
PyErr_SetFromErrnoWithFilenameObjects flow, before any filename null check or
decode_fsdefault_and_size call. Preserve vm.check_signals()? behavior, then
decode the filename only after signal handling completes.
🤖 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.

Outside diff comments:
In `@crates/capi/src/pyerrors/errno.rs`:
- Around line 23-34: Move the errno == libc::EINTR handling immediately after
retrieving errno in the PyErr_SetFromErrnoWithFilenameObjects flow, before any
filename null check or decode_fsdefault_and_size call. Preserve
vm.check_signals()? behavior, then decode the filename only after signal
handling completes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 415416af-d06c-4cb0-bcad-3eb97d1d62a2

📥 Commits

Reviewing files that changed from the base of the PR and between 2c47ef9 and 29f68a9.

📒 Files selected for processing (2)
  • crates/capi/src/pyerrors/errno.rs
  • crates/capi/src/pyerrors/windows.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/capi/src/pyerrors/windows.rs

@bschoenmaeckers
bschoenmaeckers force-pushed the c-api-more-error branch 2 times, most recently from 7c47ed9 to 1fdf316 Compare July 17, 2026 07:48
Add windows error functions

Implement unicode error functions

Add errno functions to c-api

Spell

Review

Refactor windows errors

Review

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🤖 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 545-555: Update the PyErr_SetExcInfo handling around exc_val and
vm.set_exception so Py_None is treated the same as NULL, producing an empty
handled-exception state instead of being downcast as PyBaseException. Preserve
the existing TypeError for non-None values that are not exception instances, and
keep valid exception restoration unchanged.
- Around line 481-486: Update PyErr_GetHandledException and PyErr_GetExcInfo to
use topmost_exception() instead of current_exception(), so both APIs find the
oldest active handled exception past empty stack slots. Preserve
PyErr_GetExcInfo’s existing behavior of writing NULL values when no exception is
active; apply the changes at crates/capi/src/pyerrors.rs lines 481-486 and
507-535.
- Around line 490-497: Update PyErr_SetHandledException in
crates/capi/src/pyerrors.rs lines 490-497 to retain the incoming exception
rather than consume the raw pointer, while clearing handled-exception state for
NULL or Py_None. In the corresponding handled-exception setter at
crates/capi/src/pyerrors.rs lines 545-555, continue stealing type and traceback,
but treat value == NULL or Py_None as an empty handled state instead of
downcasting it.
🪄 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: 16ddb047-c75b-4bd8-b995-5381c46e5117

📥 Commits

Reviewing files that changed from the base of the PR and between 1fdf316 and dbfcbc8.

📒 Files selected for processing (5)
  • crates/capi/src/pyerrors.rs
  • crates/capi/src/pyerrors/errno.rs
  • crates/capi/src/pyerrors/unicode.rs
  • crates/capi/src/pyerrors/windows.rs
  • crates/capi/src/util.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/capi/src/util.rs
  • crates/capi/src/pyerrors/windows.rs
  • crates/capi/src/pyerrors/errno.rs
  • crates/capi/src/pyerrors/unicode.rs

Comment thread crates/capi/src/pyerrors.rs
Comment thread crates/capi/src/pyerrors.rs
Comment thread crates/capi/src/pyerrors.rs

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/capi/src/pyerrors.rs (1)

508-535: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fix memory leaks when out-pointers are NULL.

When ptype, pvalue, or ptraceback is passed as NULL, the corresponding PyObjectRef references obtained from vm.split_exception() are converted to raw pointers via into_raw().as_ptr() but never written out or decremented. This causes the objects to leak because their reference counts are never decremented.

Keep the references as Option<PyObjectRef> until they are actually written to the non-NULL destination pointers. This ensures that any discarded references automatically decrement their reference counts when the options are dropped at the end of the block.

🐛 Proposed fix
-        let (ty, value, tb) = vm.topmost_exception().map_or_else(
-            || {
-                (
-                    core::ptr::null_mut(),
-                    core::ptr::null_mut(),
-                    core::ptr::null_mut(),
-                )
-            },
-            |exc| {
-                let (ty, value, tb) = vm.split_exception(exc);
-                let tb = if vm.is_none(&tb) {
-                    core::ptr::null_mut()
-                } else {
-                    tb.into_raw().as_ptr()
-                };
-                (ty.into_raw().as_ptr(), value.into_raw().as_ptr(), tb)
-            },
-        );
-
-        if let Some(ptype) = NonNull::new(ptype) {
-            unsafe { ptype.write(ty) };
-        }
-        if let Some(pvalue) = NonNull::new(pvalue) {
-            unsafe { pvalue.write(value) };
-        }
-        if let Some(ptraceback) = NonNull::new(ptraceback) {
-            unsafe { ptraceback.write(tb) };
-        }
+        let (ty, value, tb) = vm.topmost_exception().map_or_else(
+            || (None, None, None),
+            |exc| {
+                let (ty, value, tb) = vm.split_exception(exc);
+                let tb = if vm.is_none(&tb) { None } else { Some(tb) };
+                (Some(ty), Some(value), tb)
+            },
+        );
+
+        if let Some(ptype) = NonNull::new(ptype) {
+            unsafe { ptype.write(ty.map_or(core::ptr::null_mut(), |x| x.into_raw().as_ptr())) };
+        }
+        if let Some(pvalue) = NonNull::new(pvalue) {
+            unsafe { pvalue.write(value.map_or(core::ptr::null_mut(), |x| x.into_raw().as_ptr())) };
+        }
+        if let Some(ptraceback) = NonNull::new(ptraceback) {
+            unsafe { ptraceback.write(tb.map_or(core::ptr::null_mut(), |x| x.into_raw().as_ptr())) };
+        }

Note: Please ensure PyErr_Fetch (around line 416) is also updated if it uses the same pattern of calling into_raw() before checking for NULL pointers, as this would result in identical memory leaks.

🤖 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 508 - 535, Update the
exception-fetching logic around vm.split_exception and PyErr_Fetch to retain
each extracted PyObjectRef as an Option until its corresponding out-pointer is
validated; only call into_raw when ptype, pvalue, or ptraceback is non-NULL,
allowing unconsumed references to drop and decrement their counts.
🤖 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.

Outside diff comments:
In `@crates/capi/src/pyerrors.rs`:
- Around line 508-535: Update the exception-fetching logic around
vm.split_exception and PyErr_Fetch to retain each extracted PyObjectRef as an
Option until its corresponding out-pointer is validated; only call into_raw when
ptype, pvalue, or ptraceback is non-NULL, allowing unconsumed references to drop
and decrement their counts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 366486ed-8a81-4a52-b219-d5ab3beef433

📥 Commits

Reviewing files that changed from the base of the PR and between dbfcbc8 and 122fd5a.

📒 Files selected for processing (2)
  • crates/capi/src/pyerrors.rs
  • crates/vm/src/vm/mod.rs

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