Skip to content

Add codecs support to c-api#8267

Merged
youknowone merged 2 commits into
RustPython:mainfrom
bschoenmaeckers:c-api-codecs
Jul 16, 2026
Merged

Add codecs support to c-api#8267
youknowone merged 2 commits into
RustPython:mainfrom
bschoenmaeckers:c-api-codecs

Conversation

@bschoenmaeckers

@bschoenmaeckers bschoenmaeckers commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Expanded the C API with codec registration/discovery, encode/decode, incremental encoder/decoder, and stream reader/writer support.
    • Added codec error-handler registration/lookup, including built-in strategies (strict/ignore/replace and variants).
    • Exposed a public codecs module in the C API.
  • Bug Fixes
    • Standardized handling of C-string inputs (null/UTF-8) across C-API entry points for more consistent error propagation and behavior.
    • Improved C-API object pointer handling for safer borrowing/casting at the FFI boundary.
  • Chores
    • Minor formatting/comment cleanup.

@bschoenmaeckers bschoenmaeckers changed the title Add codecs support to c-apo Add codecs support to c-api Jul 13, 2026
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds C-ABI codec registry operations, shared VM-aware C-string and raw-pointer conversion helpers, and migrates existing C-API entry points to those helpers.

Changes

C API codec bridge

Layer / File(s) Summary
Codec registry bridge
crates/capi/src/codecs.rs, crates/capi/src/lib.rs
Exports codec registration, lookup, encoding/decoding, incremental and stream construction, and error-handler functions backed by codec_registry.

Shared FFI conversion

Layer / File(s) Summary
Conversion contracts
crates/capi/src/util.rs
Adds VM-aware C-string decoding and raw PyObject ownership and borrowing helpers.

Existing C-API migration

Layer / File(s) Summary
String argument migration
crates/capi/src/abstract_*.rs, crates/capi/src/dictobject.rs, crates/capi/src/object.rs, crates/capi/src/descrobject.rs, crates/capi/src/methodobject.rs, crates/capi/src/import.rs, crates/capi/src/pycapsule.rs, crates/capi/src/pyerrors.rs, crates/capi/src/unicodeobject.rs, crates/capi/src/warnings.rs
Uses CStrExt for C-string keys, attributes, metadata, imports, errors, warnings, capsules, and Unicode arguments.

|Raw-pointer migration
crates/capi/src/{abstract_*,boolobject.rs,bytearrayobject.rs,bytesobject.rs,ceval.rs,dictobject.rs,listobject.rs,longobject.rs,moduleobject.rs,object.rs,pyerrors.rs,refcount.rs,setobject.rs,sliceobject.rs,tupleobject.rs,unicodeobject.rs,weakrefobject.rs}|Uses FfiPtrExt for borrowed casts, optional pointers, and owned pointer conversion across C-API entry points.|

|FFI declarations and cleanup
crates/capi/src/object/pytype.rs, crates/vm/src/sequence.rs|Changes selected PyType_* pointer parameters to mutable pointers and removes trailing whitespace from a TODO comment.|

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

Sequence Diagram(s)

sequenceDiagram
  participant CCaller
  participant CodecCAPI
  participant CodecRegistry
  participant CodecImplementation
  CCaller->>CodecCAPI: invoke PyCodec_* function
  CodecCAPI->>CodecRegistry: lookup or execute codec operation
  CodecRegistry->>CodecImplementation: call codec or error handler
  CodecImplementation-->>CodecRegistry: return result
  CodecRegistry-->>CodecCAPI: return Python object or status
  CodecCAPI-->>CCaller: return C-ABI result
Loading

Possibly related PRs

Suggested reviewers: youknowone, shaharnaveh

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 91.30% which is sufficient. The required threshold is 80.00%.
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.
Title check ✅ Passed The title clearly summarizes the main change: adding codecs support to the C API.
✨ 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.

@youknowone youknowone left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

could you also check if other boilerplates can be also refactored?

Comment thread crates/capi/src/codecs.rs Outdated
} else {
unsafe { CStr::from_ptr(encoding) }
.to_str()
.map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please add a helper for this commonly used error

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.

I've added a helper. What do you think?

@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: 2

🧹 Nitpick comments (2)
crates/capi/src/util.rs (1)

232-239: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fix typographical error in the exception message.

The message "argument of must be valid UTF-8" appears to be missing a word or incorrectly includes "of". Consider changing it to "argument must be valid UTF-8".

♻️ Proposed fix
 impl<'a> CStrExt<'a> for *mut c_char {
     fn try_as_str(self, vm: &VirtualMachine) -> PyResult<Option<&'a str>> {
         NonNull::new(self)
             .map(|ptr| unsafe { CStr::from_ptr(ptr.as_ptr()) }.to_str())
             .transpose()
-            .map_err(|_| vm.new_system_error("argument of must be valid UTF-8"))
+            .map_err(|_| vm.new_system_error("argument must be valid UTF-8"))
     }
 }
🤖 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 232 - 239, Update the error message in
CStrExt::try_as_str to remove the extraneous “of”, so invalid UTF-8 reports that
the argument must be valid UTF-8.
crates/capi/src/import.rs (1)

52-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Simplify null pointer check.

Since try_as_str(vm)? naturally handles null pointers by returning Option::None, you can simplify this block using if let instead of explicitly checking for .is_null() and calling .unwrap().

♻️ Proposed fix
-        if !pathname.is_null() {
-            let pathname = pathname.try_as_str(vm)?.unwrap();
+        if let Some(pathname) = pathname.try_as_str(vm)? {
             module.set_attr("__file__", vm.ctx.new_str(pathname), vm)?;
         }
🤖 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/import.rs` around lines 52 - 53, Update the pathname handling
block to use `if let Some(pathname) = pathname.try_as_str(vm)?` directly,
removing the explicit `is_null()` check and `unwrap()` while preserving the
existing processing for valid pathnames.
🤖 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/descrobject.rs`:
- Around line 229-234: Update PyDescr_NewMember to return the result of
member.build directly from the with_vm closure, removing the wrapping Ok so the
existing with_vm conversion path receives the expected result type.

In `@crates/capi/src/pyerrors.rs`:
- Around line 211-216: Update the exception-name parsing in the with_vm closure
to propagate the PyResult from try_as_str(vm) with ?, removing the expect that
panics on invalid UTF-8; preserve the existing unwrap and rsplit_once validation
behavior.

---

Nitpick comments:
In `@crates/capi/src/import.rs`:
- Around line 52-53: Update the pathname handling block to use `if let
Some(pathname) = pathname.try_as_str(vm)?` directly, removing the explicit
`is_null()` check and `unwrap()` while preserving the existing processing for
valid pathnames.

In `@crates/capi/src/util.rs`:
- Around line 232-239: Update the error message in CStrExt::try_as_str to remove
the extraneous “of”, so invalid UTF-8 reports that the argument must be valid
UTF-8.
🪄 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: f7f94de3-6e6c-4046-b9cc-1149b6a5215f

📥 Commits

Reviewing files that changed from the base of the PR and between 26a957c and ecb2046.

📒 Files selected for processing (14)
  • crates/capi/src/abstract_.rs
  • crates/capi/src/abstract_/mapping.rs
  • crates/capi/src/codecs.rs
  • crates/capi/src/descrobject.rs
  • crates/capi/src/dictobject.rs
  • crates/capi/src/import.rs
  • crates/capi/src/lib.rs
  • crates/capi/src/methodobject.rs
  • crates/capi/src/object.rs
  • crates/capi/src/pycapsule.rs
  • crates/capi/src/pyerrors.rs
  • crates/capi/src/util.rs
  • crates/capi/src/warnings.rs
  • crates/vm/src/sequence.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/capi/src/lib.rs
  • crates/capi/src/codecs.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

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🧹 Nitpick comments (2)
crates/capi/src/util.rs (1)

232-239: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fix typographical error in the exception message.

The message "argument of must be valid UTF-8" appears to be missing a word or incorrectly includes "of". Consider changing it to "argument must be valid UTF-8".

♻️ Proposed fix
 impl<'a> CStrExt<'a> for *mut c_char {
     fn try_as_str(self, vm: &VirtualMachine) -> PyResult<Option<&'a str>> {
         NonNull::new(self)
             .map(|ptr| unsafe { CStr::from_ptr(ptr.as_ptr()) }.to_str())
             .transpose()
-            .map_err(|_| vm.new_system_error("argument of must be valid UTF-8"))
+            .map_err(|_| vm.new_system_error("argument must be valid UTF-8"))
     }
 }
🤖 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 232 - 239, Update the error message in
CStrExt::try_as_str to remove the extraneous “of”, so invalid UTF-8 reports that
the argument must be valid UTF-8.
crates/capi/src/import.rs (1)

52-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Simplify null pointer check.

Since try_as_str(vm)? naturally handles null pointers by returning Option::None, you can simplify this block using if let instead of explicitly checking for .is_null() and calling .unwrap().

♻️ Proposed fix
-        if !pathname.is_null() {
-            let pathname = pathname.try_as_str(vm)?.unwrap();
+        if let Some(pathname) = pathname.try_as_str(vm)? {
             module.set_attr("__file__", vm.ctx.new_str(pathname), vm)?;
         }
🤖 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/import.rs` around lines 52 - 53, Update the pathname handling
block to use `if let Some(pathname) = pathname.try_as_str(vm)?` directly,
removing the explicit `is_null()` check and `unwrap()` while preserving the
existing processing for valid pathnames.
🤖 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/descrobject.rs`:
- Around line 229-234: Update PyDescr_NewMember to return the result of
member.build directly from the with_vm closure, removing the wrapping Ok so the
existing with_vm conversion path receives the expected result type.

In `@crates/capi/src/pyerrors.rs`:
- Around line 211-216: Update the exception-name parsing in the with_vm closure
to propagate the PyResult from try_as_str(vm) with ?, removing the expect that
panics on invalid UTF-8; preserve the existing unwrap and rsplit_once validation
behavior.

---

Nitpick comments:
In `@crates/capi/src/import.rs`:
- Around line 52-53: Update the pathname handling block to use `if let
Some(pathname) = pathname.try_as_str(vm)?` directly, removing the explicit
`is_null()` check and `unwrap()` while preserving the existing processing for
valid pathnames.

In `@crates/capi/src/util.rs`:
- Around line 232-239: Update the error message in CStrExt::try_as_str to remove
the extraneous “of”, so invalid UTF-8 reports that the argument must be valid
UTF-8.
🪄 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: f7f94de3-6e6c-4046-b9cc-1149b6a5215f

📥 Commits

Reviewing files that changed from the base of the PR and between 26a957c and ecb2046.

📒 Files selected for processing (14)
  • crates/capi/src/abstract_.rs
  • crates/capi/src/abstract_/mapping.rs
  • crates/capi/src/codecs.rs
  • crates/capi/src/descrobject.rs
  • crates/capi/src/dictobject.rs
  • crates/capi/src/import.rs
  • crates/capi/src/lib.rs
  • crates/capi/src/methodobject.rs
  • crates/capi/src/object.rs
  • crates/capi/src/pycapsule.rs
  • crates/capi/src/pyerrors.rs
  • crates/capi/src/util.rs
  • crates/capi/src/warnings.rs
  • crates/vm/src/sequence.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/capi/src/lib.rs
  • crates/capi/src/codecs.rs
🛑 Comments failed to post (2)
crates/capi/src/descrobject.rs (1)

229-234: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

set -euo pipefail
printf '\n== descrobject.rs excerpt ==\n'
sed -n '200,260p' crates/capi/src/descrobject.rs

printf '\n== with_vm definition/usages ==\n'
rg -n "fn with_vm|with_vm\(" -S crates | sed -n '1,120p'

printf '\n== build() signatures near PyMemberDescriptor ==\n'
rg -n "build\(" crates/capi/src/descrobject.rs crates -S | sed -n '1,120p'

Repository: RustPython/RustPython

Length of output: 12962


🏁 Script executed:

set -euo pipefail
echo '=== descrobject.rs (220-240) ==='
sed -n '220,240p' crates/capi/src/descrobject.rs

echo
echo '=== search with_vm definition ==='
rg -n "pub .*with_vm|fn with_vm|with_vm\s*\(" crates -S | sed -n '1,120p'

echo
echo '=== search build signature for PyMemberDef ==='
rg -n "struct PyMemberDef|impl .*PyMemberDef|build\(.*PyTypeObject|PyResult<PyRef<PyMemberDescriptor>>" crates -S | sed -n '1,160p'

Repository: RustPython/RustPython

Length of output: 9132


🏁 Script executed:

set -euo pipefail

echo '=== crates/vm/src/vm/thread.rs (with_vm) ==='
sed -n '740,810p' crates/vm/src/vm/thread.rs

echo
echo '=== crates/capi/src/descrobject.rs (PyMemberDef build) ==='
sed -n '120,155p' crates/capi/src/descrobject.rs

echo
echo '=== nearby patterns in crates/capi/src ==='
rg -n "with_vm\\(\\|.*Ok\\(.*build\\(" crates/capi/src -S | sed -n '1,120p'

Repository: RustPython/RustPython

Length of output: 4154


🏁 Script executed:

set -euo pipefail

echo '=== crates/capi/src/descrobject.rs (PyGetSetDef build) ==='
sed -n '1,80p' crates/capi/src/descrobject.rs

echo
echo '=== crates/capi/src/descrobject.rs (PyMemberDef build continued) ==='
sed -n '80,180p' crates/capi/src/descrobject.rs

echo
echo '=== functions in descrobject.rs using with_vm + Ok(...) ==='
rg -n "with_vm\\(\\|\\s*Ok\\(" crates/capi/src/descrobject.rs -n -S

Repository: RustPython/RustPython

Length of output: 7393


🏁 Script executed:

set -euo pipefail

echo '=== crates/capi/src/descrobject.rs (1-180) ==='
sed -n '1,180p' crates/capi/src/descrobject.rs

echo
echo '=== other with_vm + Ok(...) patterns in capi ==='
rg -n "with_vm\\(\\|\\s*Ok\\(" crates/capi/src -S | sed -n '1,200p'

Repository: RustPython/RustPython

Length of output: 7223


🏁 Script executed:

set -euo pipefail

echo '=== crates/capi/src/pystate.rs ==='
sed -n '1,220p' crates/capi/src/pystate.rs

echo
echo '=== direct uses of crate::pystate::with_vm in capi ==='
rg -n "crate::pystate::with_vm|with_vm\\(" crates/capi/src -S | sed -n '1,220p'

Repository: RustPython/RustPython

Length of output: 18885


🏁 Script executed:

set -euo pipefail

echo '=== crates/capi/src/util.rs ==='
sed -n '1,220p' crates/capi/src/util.rs

echo
echo '=== FfiResult implementations ==='
rg -n "trait FfiResult|impl .*FfiResult" crates/capi/src crates -S | sed -n '1,220p'

Repository: RustPython/RustPython

Length of output: 7911


Return build() directly in PyDescr_NewMember. The extra Ok(...) adds a nested Result and breaks the with_vm conversion path here.

🤖 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/descrobject.rs` around lines 229 - 234, Update
PyDescr_NewMember to return the result of member.build directly from the with_vm
closure, removing the wrapping Ok so the existing with_vm conversion path
receives the expected result type.
crates/capi/src/pyerrors.rs (1)

211-216: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Propagate the PyResult rather than panicking.

Calling .expect("...") on try_as_str(vm) triggers a Rust panic if the input is not valid UTF-8. Since we are inside a with_vm closure, propagating the result via ? will correctly translate the failure into a Python exception state.

♻️ Proposed fix
         let (module, name) = name
-            .try_as_str(vm)
-            .expect("Exception name is not valid UTF-8")
-            .unwrap()
+            .try_as_str(vm)?
+            .unwrap()
             .rsplit_once('.')
             .expect("Exception name must be of the form 'module.ExceptionName'");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        let (module, name) = name
            .try_as_str(vm)?
            .unwrap()
            .rsplit_once('.')
            .expect("Exception name must be of the form 'module.ExceptionName'");
🤖 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 211 - 216, Update the
exception-name parsing in the with_vm closure to propagate the PyResult from
try_as_str(vm) with ?, removing the expect that panics on invalid UTF-8;
preserve the existing unwrap and rsplit_once validation behavior.

@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

🤖 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/util.rs`:
- Around line 226-244: Mark CStrExt::try_as_str as unsafe in the trait and both
pointer implementations, and update callers to invoke it within explicit unsafe
blocks. Correct the system error text in the mutable-pointer implementation from
“argument of must be valid UTF-8” to “argument must be valid UTF-8”, preserving
the existing UTF-8 handling.
🪄 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: 38cc1c00-e6fb-449e-8f8e-15f391f61a15

📥 Commits

Reviewing files that changed from the base of the PR and between ecb2046 and b05d0b0.

📒 Files selected for processing (13)
  • crates/capi/src/abstract_.rs
  • crates/capi/src/abstract_/mapping.rs
  • crates/capi/src/codecs.rs
  • crates/capi/src/descrobject.rs
  • crates/capi/src/dictobject.rs
  • crates/capi/src/import.rs
  • crates/capi/src/methodobject.rs
  • crates/capi/src/object.rs
  • crates/capi/src/pycapsule.rs
  • crates/capi/src/pyerrors.rs
  • crates/capi/src/util.rs
  • crates/capi/src/warnings.rs
  • crates/vm/src/sequence.rs
🚧 Files skipped from review as they are similar to previous changes (12)
  • crates/vm/src/sequence.rs
  • crates/capi/src/methodobject.rs
  • crates/capi/src/import.rs
  • crates/capi/src/pycapsule.rs
  • crates/capi/src/descrobject.rs
  • crates/capi/src/dictobject.rs
  • crates/capi/src/object.rs
  • crates/capi/src/warnings.rs
  • crates/capi/src/pyerrors.rs
  • crates/capi/src/abstract_/mapping.rs
  • crates/capi/src/abstract_.rs
  • crates/capi/src/codecs.rs

Comment thread crates/capi/src/util.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.

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

74-84: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Simplify null-pointer handling by leveraging try_as_str.

The try_as_str(vm) method natively handles null pointers by returning Ok(None). You can remove the manual is_null() branches and simplify these blocks using unwrap_or and map, matching the clean pattern already established in codecs.rs and warnings.rs.

  • crates/capi/src/unicodeobject.rs#L74-L84: Replace the manual is_null() blocks for encoding and errors with unwrap_or and map.
  • crates/capi/src/unicodeobject.rs#L177-L187: Replace the identical manual is_null() blocks with the same simplified pattern.
♻️ Proposed refactor

Apply this pattern to both locations:

-        let encoding = if encoding.is_null() {
-            "utf-8"
-        } else {
-            encoding.try_as_str(vm)?.unwrap()
-        };
-        let errors = if errors.is_null() {
-            None
-        } else {
-            let errors = errors.try_as_str(vm)?.unwrap();
-            Some(vm.ctx.new_utf8_str(errors))
-        };
+        let encoding = encoding.try_as_str(vm)?.unwrap_or("utf-8");
+        let errors = errors.try_as_str(vm)?.map(|e| vm.ctx.new_utf8_str(e));
🤖 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/unicodeobject.rs` around lines 74 - 84, In both
encoding/error handling blocks in crates/capi/src/unicodeobject.rs (lines 74-84
and 177-187), replace the manual is_null branches with try_as_str(vm), using
unwrap_or for the UTF-8 encoding default and map to construct the optional error
string. Apply the same simplified pattern at both sites.
🤖 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/unicodeobject.rs`:
- Around line 74-84: In both encoding/error handling blocks in
crates/capi/src/unicodeobject.rs (lines 74-84 and 177-187), replace the manual
is_null branches with try_as_str(vm), using unwrap_or for the UTF-8 encoding
default and map to construct the optional error string. Apply the same
simplified pattern at both sites.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: e3519d9d-c593-4140-8c29-3c797097d6dd

📥 Commits

Reviewing files that changed from the base of the PR and between b05d0b0 and d787215.

📒 Files selected for processing (14)
  • crates/capi/src/abstract_.rs
  • crates/capi/src/abstract_/mapping.rs
  • crates/capi/src/codecs.rs
  • crates/capi/src/descrobject.rs
  • crates/capi/src/dictobject.rs
  • crates/capi/src/import.rs
  • crates/capi/src/methodobject.rs
  • crates/capi/src/object.rs
  • crates/capi/src/pycapsule.rs
  • crates/capi/src/pyerrors.rs
  • crates/capi/src/unicodeobject.rs
  • crates/capi/src/util.rs
  • crates/capi/src/warnings.rs
  • crates/vm/src/sequence.rs
🚧 Files skipped from review as they are similar to previous changes (9)
  • crates/vm/src/sequence.rs
  • crates/capi/src/util.rs
  • crates/capi/src/pycapsule.rs
  • crates/capi/src/dictobject.rs
  • crates/capi/src/methodobject.rs
  • crates/capi/src/import.rs
  • crates/capi/src/descrobject.rs
  • crates/capi/src/abstract_.rs
  • crates/capi/src/codecs.rs

@bschoenmaeckers
bschoenmaeckers force-pushed the c-api-codecs branch 2 times, most recently from b20ab8a to 8f0ad5d Compare July 16, 2026 08:42

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

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

52-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use try_as_str_opt for optional C strings.

Instead of manually checking for null and calling try_as_str, you can use the newly introduced try_as_str_opt, which is designed to handle null pointers gracefully by returning None.

♻️ Proposed refactor
-        if !pathname.is_null() {
-            let pathname = unsafe { pathname.try_as_str(vm) }?;
+        if let Some(pathname) = unsafe { pathname.try_as_str_opt(vm) }? {
             module.set_attr("__file__", vm.ctx.new_str(pathname), vm)?;
         }
🤖 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/import.rs` around lines 52 - 55, Update the pathname
conversion in the import logic to use try_as_str_opt instead of manually
checking pathname for null and calling try_as_str. Preserve setting the
"__file__" attribute only when the optional conversion returns Some, while
propagating conversion errors as before.
🤖 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/import.rs`:
- Around line 52-55: Update the pathname conversion in the import logic to use
try_as_str_opt instead of manually checking pathname for null and calling
try_as_str. Preserve setting the "__file__" attribute only when the optional
conversion returns Some, while propagating conversion errors as before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 6fc50b0a-725f-4fd4-acf1-b4f3a13feb7a

📥 Commits

Reviewing files that changed from the base of the PR and between d787215 and 8f0ad5d.

📒 Files selected for processing (14)
  • crates/capi/src/abstract_.rs
  • crates/capi/src/abstract_/mapping.rs
  • crates/capi/src/codecs.rs
  • crates/capi/src/descrobject.rs
  • crates/capi/src/dictobject.rs
  • crates/capi/src/import.rs
  • crates/capi/src/methodobject.rs
  • crates/capi/src/object.rs
  • crates/capi/src/pycapsule.rs
  • crates/capi/src/pyerrors.rs
  • crates/capi/src/unicodeobject.rs
  • crates/capi/src/util.rs
  • crates/capi/src/warnings.rs
  • crates/vm/src/sequence.rs
🚧 Files skipped from review as they are similar to previous changes (12)
  • crates/vm/src/sequence.rs
  • crates/capi/src/abstract_.rs
  • crates/capi/src/pycapsule.rs
  • crates/capi/src/warnings.rs
  • crates/capi/src/descrobject.rs
  • crates/capi/src/methodobject.rs
  • crates/capi/src/pyerrors.rs
  • crates/capi/src/unicodeobject.rs
  • crates/capi/src/object.rs
  • crates/capi/src/dictobject.rs
  • crates/capi/src/abstract_/mapping.rs
  • crates/capi/src/codecs.rs

@bschoenmaeckers

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 16, 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.

🧹 Nitpick comments (2)
crates/capi/src/listobject.rs (1)

78-78: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use assume_borrowed() for uniform pointer dereferencing.

To fully align with the PR's objective of standardizing raw pointer access via FfiPtrExt, consider replacing the remaining unsafe { &*item } dereferences with unsafe { item.assume_borrowed() }.

♻️ Proposed refactor for `PyList_Append` and other functions

For PyList_Append:

-        let item = unsafe { &*item }.to_owned();
+        let item = unsafe { item.assume_borrowed() }.to_owned();

Apply similar updates to PyList_Insert and PyList_SetSlice further down in the file:

// In PyList_Insert:
-        let item = unsafe { &*item }.to_owned();
+        let item = unsafe { item.assume_borrowed() }.to_owned();

// In PyList_SetSlice:
-        let items: Vec<PyObjectRef> = unsafe { &*itemlist }.try_to_value(vm)?;
+        let items: Vec<PyObjectRef> = unsafe { itemlist.assume_borrowed() }.try_to_value(vm)?;
🤖 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/listobject.rs` at line 78, Replace remaining raw pointer
dereferences in PyList_Append, PyList_Insert, and PyList_SetSlice with
FfiPtrExt::assume_borrowed(). Preserve the existing ownership conversions and
try_to_value behavior while standardizing all item/list pointer access through
the extension method.
crates/capi/src/floatobject.rs (1)

53-55: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Simplify null-checking and borrowing with assume_borrowed_or_opt.

Since FfiPtrExt provides assume_borrowed_or_opt(), you can streamline the null check and the borrow into a single step, removing the need to construct a NonNull manually.

♻️ Proposed refactor
-        let obj = NonNull::new(obj)
-            .ok_or_else(|| vm.new_type_error("float() argument must be a string or a number"))?;
-        let obj = unsafe { obj.as_ptr().assume_borrowed() }.to_owned();
+        let obj = unsafe { obj.assume_borrowed_or_opt() }
+            .ok_or_else(|| vm.new_type_error("float() argument must be a string or a number"))?
+            .to_owned();
🤖 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/floatobject.rs` around lines 53 - 55, Update the object
conversion flow in the float handling code to use
FfiPtrExt::assume_borrowed_or_opt() instead of constructing NonNull with
NonNull::new and then calling assume_borrowed(). Preserve the existing
type-error result when the pointer is null, and continue producing an owned
object for valid pointers.
🤖 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/floatobject.rs`:
- Around line 53-55: Update the object conversion flow in the float handling
code to use FfiPtrExt::assume_borrowed_or_opt() instead of constructing NonNull
with NonNull::new and then calling assume_borrowed(). Preserve the existing
type-error result when the pointer is null, and continue producing an owned
object for valid pointers.

In `@crates/capi/src/listobject.rs`:
- Line 78: Replace remaining raw pointer dereferences in PyList_Append,
PyList_Insert, and PyList_SetSlice with FfiPtrExt::assume_borrowed(). Preserve
the existing ownership conversions and try_to_value behavior while standardizing
all item/list pointer access through the extension method.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 5c58e6ee-3596-4afe-8075-3e5efd80d287

📥 Commits

Reviewing files that changed from the base of the PR and between a65d9c4 and 4f3be5d.

📒 Files selected for processing (26)
  • crates/capi/src/abstract_.rs
  • crates/capi/src/abstract_/iter.rs
  • crates/capi/src/abstract_/number.rs
  • crates/capi/src/boolobject.rs
  • crates/capi/src/bytearrayobject.rs
  • crates/capi/src/bytesobject.rs
  • crates/capi/src/ceval.rs
  • crates/capi/src/descrobject.rs
  • crates/capi/src/dictobject.rs
  • crates/capi/src/floatobject.rs
  • crates/capi/src/import.rs
  • crates/capi/src/listobject.rs
  • crates/capi/src/longobject.rs
  • crates/capi/src/methodobject.rs
  • crates/capi/src/moduleobject.rs
  • crates/capi/src/object.rs
  • crates/capi/src/object/pytype.rs
  • crates/capi/src/pyerrors.rs
  • crates/capi/src/refcount.rs
  • crates/capi/src/setobject.rs
  • crates/capi/src/sliceobject.rs
  • crates/capi/src/tupleobject.rs
  • crates/capi/src/unicodeobject.rs
  • crates/capi/src/util.rs
  • crates/capi/src/warnings.rs
  • crates/capi/src/weakrefobject.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • crates/capi/src/warnings.rs
  • crates/capi/src/descrobject.rs
  • crates/capi/src/util.rs
  • crates/capi/src/import.rs

@youknowone
youknowone merged commit 0b4e5da into RustPython:main Jul 16, 2026
50 checks passed
@bschoenmaeckers
bschoenmaeckers deleted the c-api-codecs branch July 17, 2026 07:40
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