Skip to content

Add integer c-api support#7905

Merged
youknowone merged 4 commits into
RustPython:mainfrom
bschoenmaeckers:c-api-ints
May 19, 2026
Merged

Add integer c-api support#7905
youknowone merged 4 commits into
RustPython:mainfrom
bschoenmaeckers:c-api-ints

Conversation

@bschoenmaeckers
Copy link
Copy Markdown
Contributor

@bschoenmaeckers bschoenmaeckers commented May 17, 2026

Summary by CodeRabbit

  • New Features

    • C API support for creating Python integer objects from native signed and unsigned integer types, including large unsigned types.
    • Exposed runtime checks to detect Python integer types precisely.
  • Bug Fixes

    • Consistent overflow errors when Python integers are too large to convert.
    • Defined sentinel/return behavior for FFI integer results to improve interoperability.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: b0b52f12-8315-46af-b738-d8cb087d8887

📥 Commits

Reviewing files that changed from the base of the PR and between 365c5d4 and fc76d83.

📒 Files selected for processing (2)
  • crates/capi/src/longobject.rs
  • crates/capi/src/object.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/capi/src/object.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/capi/src/longobject.rs

📝 Walkthrough

Walkthrough

Adds a new longobject cAPI module: extern "C" PyLong constructors and extractors for multiple C integer types, plus an FfiResult implementation for c_ulonglong; includes a disabled test module.

Changes

Python Long Integer FFI Bindings

Layer / File(s) Summary
Module declaration and imports
crates/capi/src/lib.rs, crates/capi/src/object.rs, crates/capi/src/longobject.rs
Exposes longobject as a public module, re-exports define_py_check crate-wide, and declares PyLong_Check/PyLong_CheckExact plus top-level imports.
PyLong constructors from C integers
crates/capi/src/longobject.rs
Six PyLong_From* extern "C" functions allocate Python ints from c_long, c_longlong, isize, usize, c_ulong, and c_ulonglong via `with_vm(
PyLong extraction to C integers
crates/capi/src/longobject.rs
Unsafe PyLong_AsLong and PyLong_AsUnsignedLongLong dereference *mut PyObject, coerce to bigint/PyInt, attempt downcast to requested C type, and map failures to VM overflow errors with fixed messages.
Disabled test validation
crates/capi/src/longobject.rs
#[cfg(false)] test module (pyo3) with disabled checks for i32 and u64 extraction; not compiled in normal builds.
FfiResult impl for c_ulonglong
crates/capi/src/util.rs
Implements FfiResult for c_ulonglong with ERR_VALUE = Self::MAX and into_output returning the input value.

Sequence Diagram(s)

sequenceDiagram
  participant C_Caller
  participant FFI_Constructor as PyLong_From*
  participant VM_Context as with_vm
  participant VM_CTX as vm.ctx
  participant PyInt as Python_int
  C_Caller->>FFI_Constructor: call PyLong_From*(C integer)
  FFI_Constructor->>VM_Context: enter VM context
  VM_Context->>VM_CTX: vm.ctx.new_int(value)
  VM_CTX->>PyInt: create Python int object
  PyInt-->>FFI_Constructor: PyObject* returned
  FFI_Constructor-->>C_Caller: PyObject*
Loading
sequenceDiagram
  participant C_Caller
  participant FFI_Extractor as PyLong_As*
  participant PyObject_Ptr
  participant BigInt_Conversion
  participant VM_Context
  C_Caller->>FFI_Extractor: call PyLong_As*(PyObject*)
  FFI_Extractor->>PyObject_Ptr: dereference & coerce
  PyObject_Ptr->>BigInt_Conversion: extract bigint value
  BigInt_Conversion->>BigInt_Conversion: try downcast to C type
  alt success
    BigInt_Conversion-->>FFI_Extractor: numeric value
    FFI_Extractor-->>C_Caller: return C integer
  else overflow/failure
    BigInt_Conversion->>VM_Context: set overflow error
    VM_Context-->>FFI_Extractor: error indicator
    FFI_Extractor-->>C_Caller: error sentinel (ERR_VALUE)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • youknowone
  • ShaharNaveh

Poem

🐰 I hop from C to Python, numbers in my paw,

I stitch longs with care and check for overflow,
From Rusty roots I craft each tiny int,
A FFI bridge where rabbits dream and sprint,
Hooray — the bindings sing, and onward we go.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add integer c-api support' directly and accurately summarizes the main purpose of the pull request: adding C-API support for Python integer objects through multiple FFI functions.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% 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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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/longobject.rs (1)

61-83: 🏗️ Heavy lift

Replace the dead test block with enabled coverage of the exported FFI helpers.

#[cfg(false)] means none of this runs, and these tests only exercise PyO3's PyInt, not the new PyLong_From* / PyLong_As* symbols. Please swap this for enabled tests around the FFI helpers so ABI regressions get caught.

🤖 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/longobject.rs` around lines 61 - 83, Replace the dead
#[cfg(false)] test module with an active #[cfg(test)] mod tests that directly
exercises the crate's exported FFI helpers (the PyLong_From* / PyLong_As*
symbols) instead of PyO3's PyInt: remove #[cfg(false)], add tests that call the
exported functions (e.g., the crate's PyLong_From<u64>-style and
PyLong_As<u64>-style helpers via their extern "C" signatures), perform
round-trip conversions (create a long from a Rust integer and extract it back)
and assert correctness so ABI/FFI regressions are caught; keep the test harness
using Python::attach or equivalent to run under the Python GIL as in the
original tests.
🤖 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/longobject.rs`:
- Around line 49-58: The function PyLong_AsUnsignedLongLong currently returns a
signed c_longlong and directly downcasts to PyInt, causing wrong behavior for
values > i64::MAX and ignoring __index__; change its return type and conversion
path to use c_ulonglong and the index protocol: in PyLong_AsUnsignedLongLong use
try_index instead of try_downcast::<PyInt>, obtain the BigInt via as_bigint()
from the indexed object, then convert using try_into::<c_ulonglong>() and map
conversion errors to vm.new_overflow_error("Python int too large to convert to C
unsigned long long"); ensure the C signature and any uses expect c_ulonglong
rather than c_longlong.

---

Nitpick comments:
In `@crates/capi/src/longobject.rs`:
- Around line 61-83: Replace the dead #[cfg(false)] test module with an active
#[cfg(test)] mod tests that directly exercises the crate's exported FFI helpers
(the PyLong_From* / PyLong_As* symbols) instead of PyO3's PyInt: remove
#[cfg(false)], add tests that call the exported functions (e.g., the crate's
PyLong_From<u64>-style and PyLong_As<u64>-style helpers via their extern "C"
signatures), perform round-trip conversions (create a long from a Rust integer
and extract it back) and assert correctness so ABI/FFI regressions are caught;
keep the test harness using Python::attach or equivalent to run under the Python
GIL as in the original tests.
🪄 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: 20f487c3-3f6e-470b-ab40-139b5fe87ea9

📥 Commits

Reviewing files that changed from the base of the PR and between 883ce9d and 444540b.

📒 Files selected for processing (2)
  • crates/capi/src/lib.rs
  • crates/capi/src/longobject.rs

Comment thread crates/capi/src/longobject.rs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
crates/capi/src/longobject.rs (1)

49-58: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use the index protocol in the unsigned extractor.

This still rejects valid __index__ providers that PyLong_AsLong accepts, and the overflow text names the signed C type. That makes PyLong_AsUnsignedLongLong observably inconsistent with the rest of this file.

Suggested fix
-        unsafe { &*obj }
-            .to_owned()
-            .try_downcast::<PyInt>(vm)?
-            .as_bigint()
-            .try_into()
-            .map_err(|_| vm.new_overflow_error("Python int too large to convert to C long long"))
+        unsafe { &*obj }
+            .to_owned()
+            .try_index(vm)?
+            .as_bigint()
+            .try_into()
+            .map_err(|_| {
+                vm.new_overflow_error("Python int too large to convert to C unsigned long long")
+            })
#!/bin/bash
set -euo pipefail

echo "Compare the signed and unsigned long extractors:"
sed -n '37,59p' crates/capi/src/longobject.rs

echo
echo "Show the coercion helpers used in this file:"
rg -n -C2 'PyLong_As|try_index\(|try_downcast::<PyInt>' crates/capi/src/longobject.rs
🤖 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/longobject.rs` around lines 49 - 58, The unsigned extractor
PyLong_AsUnsignedLongLong currently bypasses the index protocol and uses PyInt
downcast and a signed overflow message; update it to mirror the signed extractor
by using the index protocol helper (e.g., call try_index or the same helper used
by PyLong_AsLong) to coerce obj to an integer, then convert the resulting BigInt
to c_ulonglong and return vm.new_overflow_error with text naming the unsigned C
type ("Python int too large to convert to C unsigned long long") on overflow;
locate and follow the pattern in the signed extractor (PyLong_AsLong) and reuse
its coercion/try_index logic and error handling.
🤖 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/longobject.rs`:
- Around line 49-58: The unsigned extractor PyLong_AsUnsignedLongLong currently
bypasses the index protocol and uses PyInt downcast and a signed overflow
message; update it to mirror the signed extractor by using the index protocol
helper (e.g., call try_index or the same helper used by PyLong_AsLong) to coerce
obj to an integer, then convert the resulting BigInt to c_ulonglong and return
vm.new_overflow_error with text naming the unsigned C type ("Python int too
large to convert to C unsigned long long") on overflow; locate and follow the
pattern in the signed extractor (PyLong_AsLong) and reuse its coercion/try_index
logic and error handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: f50d506c-f461-4305-8a5a-c291b6a58f56

📥 Commits

Reviewing files that changed from the base of the PR and between 444540b and 901319a.

📒 Files selected for processing (2)
  • crates/capi/src/longobject.rs
  • crates/capi/src/util.rs

@youknowone youknowone merged commit 6e56d93 into RustPython:main May 19, 2026
26 checks passed
@bschoenmaeckers bschoenmaeckers deleted the c-api-ints branch May 19, 2026 07:44
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