Skip to content

Conversation

@youknowone
Copy link
Member

@youknowone youknowone commented Nov 28, 2025

Summary by CodeRabbit

  • New Features

    • Pointer type generation with automatic caching and working POINTER/pointer APIs
    • Library loading can target the current process when no name is provided
    • Simple ctypes types gain a from_param conversion method
  • Bug Fixes

    • Pointer instantiation now returns usable pointer objects
    • Function-pointer construction handles address/name forms and reports errors cleanly
    • Array construction preserves the provided type when creating instances

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 28, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Unix library loader now accepts None to obtain the current process handle; ctypes gains cached pointer-type creation and pointer-instantiation APIs exposed as POINTER/pointer; PyCSimpleType.from_param added; PyCFuncPtr constructor refactored to support empty, address, and (name,dll) forms.

Changes

Cohort / File(s) Summary
Unix library loading
crates/vm/src/stdlib/ctypes.rs
load_library_unix signature changed to accept Option<String>; None calls libc::dlopen(null, RTLD_NOW) to return current process handle; previous cache insertion preserved for Some(name).
Pointer type & instance APIs
crates/vm/src/stdlib/ctypes.rs
Added pub fn create_pointer_type(cls: PyObjectRef, vm: &VirtualMachine) -> PyResult and pub fn create_pointer_inst(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult; introduced _pointer_type_cache usage and dynamic pointer-type construction; updated POINTER and pointer bindings to these implementations.
PyCSimpleType.from_param
crates/vm/src/stdlib/ctypes/base.rs
Added #[pyclassmethod] fn from_param(cls: PyTypeRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult that returns value if already instance of cls, otherwise reads _as_parameter_ and delegates recursively or raises TypeError.
PyCFuncPtr constructor refactor
crates/vm/src/stdlib/ctypes/function.rs
Changed constructor Args from (PyTupleRef, FuncArgs) to FuncArgs; py_new now inspects args and branches: empty → uninitialized pointer, integer → treat as function address, tuple → treat as (name, dll) with library loading and symbol lookup; added error handling for invalid forms.
PyCArray construction typing
crates/vm/src/stdlib/ctypes/array.rs
py_new now accepts cls: PyTypeRef and constructs return via .into_ref_with_type(vm, cls).map(Into::into) instead of .into_pyobject(vm) to preserve type.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant PyCFuncPtr as PyCFuncPtr::py_new
    participant FormInspector as Form Branching
    participant AddrHandler as Address Path
    participant TupleHandler as Tuple (name,dll) Path
    participant LibLoader as Library Loader
    participant Result as PyCFuncPtr Construction

    Caller->>PyCFuncPtr: call py_new(args)
    PyCFuncPtr->>FormInspector: inspect args

    alt args empty
        FormInspector->>Result: create uninitialized PyCFuncPtr
    else first arg is integer
        FormInspector->>AddrHandler: extract address
        AddrHandler->>Result: build PyCFuncPtr with pointer, name, handler
    else first arg is tuple (name,dll)
        FormInspector->>TupleHandler: extract name and dll
        TupleHandler->>LibLoader: resolve library handle (load_library_unix / LoadLibrary)
        LibLoader->>LibLoader: lookup symbol by name
        LibLoader->>Result: return code pointer or None
        Result->>Result: construct PyCFuncPtr with resolved data
    else invalid form
        FormInspector->>Result: return TypeError
    end

    Result-->>Caller: PyResult<PyCFuncPtr>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Attention areas:
    • crates/vm/src/stdlib/ctypes/function.rs: branching correctness, safety around raw addresses, symbol lookup and error propagation.
    • crates/vm/src/stdlib/ctypes.rs: _pointer_type_cache concurrency/validation, type derivation correctness, public API bindings.
    • crates/vm/src/stdlib/ctypes/base.rs: recursion and attribute lookup semantics in from_param.
    • crates/vm/src/stdlib/ctypes/array.rs: ensuring correct type-ref construction and ownership.

Possibly related PRs

  • Fix import ctypes #6304 — mirrors the same ctypes changes (load_library_unix signature, create_pointer_type/create_pointer_inst, POINTER/pointer bindings).
  • _ctypes pt. 5 #5653 — overlapping ctypes subsystem modifications including Unix library-loading behavior and pointer/funcptr implementations.

Suggested reviewers

  • arihant2math

Poem

🐰
I nibble bytes where pointers grow,
Cache a type and off I go.
Address, tuple, or empty start,
Functions bound and types take part.
Hooray — ctypes hops with rabbit heart!

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Fix import ctypes' is vague and does not clearly convey the primary changes. The PR introduces significant new functionality (pointer type generation, caching, Unix/Windows library loading improvements) that goes well beyond a simple 'import' fix. Consider using a more descriptive title that captures the main purpose, such as 'Implement ctypes pointer type generation and library loading' or 'Add POINTER and pointer functions to ctypes module'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

📜 Recent review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 469c93a and 7c66122.

⛔ Files ignored due to path filters (1)
  • Lib/ctypes/__init__.py is excluded by !Lib/**
📒 Files selected for processing (4)
  • crates/vm/src/stdlib/ctypes.rs (3 hunks)
  • crates/vm/src/stdlib/ctypes/array.rs (1 hunks)
  • crates/vm/src/stdlib/ctypes/base.rs (1 hunks)
  • crates/vm/src/stdlib/ctypes/function.rs (1 hunks)

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.

return PyCSimpleType::from_param(cls, as_parameter, vm);
}

Err(vm.new_type_error("wrong type".to_string()))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Err(vm.new_type_error("wrong type".to_string()))
Err(vm.new_type_error("wrong type"))

// If None, call libc::dlopen(null, mode) to get the current process handle
let handle = unsafe { libc::dlopen(std::ptr::null(), libc::RTLD_NOW) };
if handle.is_null() {
return Err(vm.new_os_error("dlopen() error".to_string()));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return Err(vm.new_os_error("dlopen() error".to_string()));
return Err(vm.new_os_error("dlopen() error"));

@youknowone youknowone force-pushed the import-ctypes branch 2 times, most recently from 3e78ddf to 469c93a Compare November 28, 2025 15:57
@youknowone youknowone marked this pull request as ready for review November 28, 2025 16:07
Copy link
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: 2

Caution

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

⚠️ Outside diff range comments (1)
crates/vm/src/stdlib/ctypes.rs (1)

192-205: Inconsistent error handling with Unix version.

Line 203 uses .unwrap() which will panic if library loading fails, while the Unix version on lines 220-222 properly maps the error to a Python OSError. Consider consistent error handling.

     fn load_library_windows(
         name: String,
         _load_flags: OptionalArg<i32>,
         vm: &VirtualMachine,
     ) -> PyResult<usize> {
         // TODO: audit functions first
         // TODO: load_flags
         let cache = library::libcache();
         let mut cache_write = cache.write();
-        let (id, _) = cache_write.get_or_insert_lib(&name, vm).unwrap();
+        let (id, _) = cache_write
+            .get_or_insert_lib(&name, vm)
+            .map_err(|e| vm.new_os_error(e.to_string()))?;
         Ok(id)
     }
🧹 Nitpick comments (1)
crates/vm/src/stdlib/ctypes.rs (1)

245-283: Implementation is correct, but consider caching the module lookup.

The function works correctly for creating and caching pointer types. However, vm.import("_ctypes", 0)? is called on every invocation. For frequently-called code paths, this could be optimized by accessing the module through vm context directly if available.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fef9de2 and 469c93a.

⛔ Files ignored due to path filters (1)
  • Lib/ctypes/__init__.py is excluded by !Lib/**
📒 Files selected for processing (3)
  • crates/vm/src/stdlib/ctypes.rs (3 hunks)
  • crates/vm/src/stdlib/ctypes/base.rs (1 hunks)
  • crates/vm/src/stdlib/ctypes/function.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Follow the default rustfmt code style in Rust code (cargo fmt to format)
Always run clippy to lint Rust code (cargo clippy) before completing tasks and fix any warnings or lints introduced by your changes
Follow Rust best practices for error handling and memory management

Files:

  • crates/vm/src/stdlib/ctypes/function.rs
  • crates/vm/src/stdlib/ctypes/base.rs
  • crates/vm/src/stdlib/ctypes.rs
**/src/**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

Files:

  • crates/vm/src/stdlib/ctypes/function.rs
  • crates/vm/src/stdlib/ctypes/base.rs
  • crates/vm/src/stdlib/ctypes.rs
🧬 Code graph analysis (1)
crates/vm/src/stdlib/ctypes/function.rs (1)
crates/vm/src/stdlib/ctypes/base.rs (2)
  • py_new (214-242)
  • new (167-173)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run snippets and cpython tests (windows-latest)
  • GitHub Check: Run rust tests (windows-latest)
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
🔇 Additional comments (4)
crates/vm/src/stdlib/ctypes/base.rs (1)

175-188: LGTM! The from_param implementation follows CPython's pattern correctly.

The method properly handles:

  1. Direct instance check with early return
  2. Fallback to _as_parameter_ attribute lookup
  3. Recursive conversion via from_param
crates/vm/src/stdlib/ctypes.rs (2)

225-232: LGTM! Proper handling for None to get current process handle.

The implementation correctly uses libc::dlopen(null, RTLD_NOW) to obtain the current process handle when name is None, with appropriate null-check error handling. The error message now matches the past review suggestion.


286-295: LGTM!

Clean delegation pattern that correctly:

  1. Gets the object's type
  2. Creates the corresponding pointer type via create_pointer_type
  3. Instantiates the pointer with the object
crates/vm/src/stdlib/ctypes/function.rs (1)

195-201: The review comment is incorrect: trailing null termination is explicitly allowed and potentially beneficial in libloading.

According to libloading's documentation, Library::get() accepts symbol names with optional trailing NUL bytes (e.g., b"foo\0"), and the trailing NUL is explicitly noted as potentially avoiding an allocation. The current code pattern format!("{}\0", &name) followed by lib.get(terminated.as_bytes()) is valid and may be preferable to the suggested name.as_bytes() for efficiency reasons.

The concern that "the library expects a clean symbol name" is unfounded—trailing NUL bytes are explicitly documented as allowed and do not cause issues.

Comment on lines 147 to 161
if let Ok(addr) = first_arg.try_int(vm) {
let ptr_val = addr.as_bigint().to_usize().unwrap_or(0);
return Ok(Self {
ptr: PyRwLock::new(Some(CodePtr(ptr_val as *mut _))),
needs_free: AtomicCell::new(false),
arg_types: PyRwLock::new(None),
_flags_: AtomicCell::new(0),
res_type: PyRwLock::new(None),
name: PyRwLock::new(Some(format!("CFuncPtr@{:#x}", ptr_val))),
handler: vm.ctx.new_int(ptr_val).into(),
}
.to_pyobject(vm));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Silent fallback to address 0 may mask invalid input.

When to_usize() fails (e.g., negative or oversized integer), the code silently uses address 0, which will likely cause a crash or undefined behavior when the function is called later. Consider returning an error instead.

         if let Ok(addr) = first_arg.try_int(vm) {
-            let ptr_val = addr.as_bigint().to_usize().unwrap_or(0);
+            let ptr_val = addr
+                .as_bigint()
+                .to_usize()
+                .ok_or_else(|| vm.new_value_error("Invalid function address".to_string()))?;
             return Ok(Self {
📝 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.

Suggested change
if let Ok(addr) = first_arg.try_int(vm) {
let ptr_val = addr.as_bigint().to_usize().unwrap_or(0);
return Ok(Self {
ptr: PyRwLock::new(Some(CodePtr(ptr_val as *mut _))),
needs_free: AtomicCell::new(false),
arg_types: PyRwLock::new(None),
_flags_: AtomicCell::new(0),
res_type: PyRwLock::new(None),
name: PyRwLock::new(Some(format!("CFuncPtr@{:#x}", ptr_val))),
handler: vm.ctx.new_int(ptr_val).into(),
}
.to_pyobject(vm));
}
if let Ok(addr) = first_arg.try_int(vm) {
let ptr_val = addr
.as_bigint()
.to_usize()
.ok_or_else(|| vm.new_value_error("Invalid function address".to_string()))?;
return Ok(Self {
ptr: PyRwLock::new(Some(CodePtr(ptr_val as *mut _))),
needs_free: AtomicCell::new(false),
arg_types: PyRwLock::new(None),
_flags_: AtomicCell::new(0),
res_type: PyRwLock::new(None),
name: PyRwLock::new(Some(format!("CFuncPtr@{:#x}", ptr_val))),
handler: vm.ctx.new_int(ptr_val).into(),
}
.to_pyobject(vm));
}
🤖 Prompt for AI Agents
In crates/vm/src/stdlib/ctypes/function.rs around lines 147-159, the code
currently uses addr.as_bigint().to_usize().unwrap_or(0) which silently falls
back to address 0 on conversion failure; instead match on to_usize() and when it
returns None return an Err created from the VM (e.g., vm.new_value_error or
vm.new_type_error) with a clear message about invalid pointer value, so the
function creation aborts rather than constructing a CFuncPtr@0.

@youknowone youknowone merged commit 23ec5a5 into RustPython:main Nov 28, 2025
10 of 13 checks passed
@youknowone youknowone deleted the import-ctypes branch November 28, 2025 16:32
@coderabbitai coderabbitai bot mentioned this pull request Nov 29, 2025
This was referenced Dec 11, 2025
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