Skip to content

Commit 0d4c31a

Browse files
committed
Use cached ctypes argtypes for ordinary function calls
Match CPython class metadata fallback in call preparation and the argtypes getter. Extend existing callback metadata regressions to native-address instances, missing class-local metadata, and instance override reset. Assisted-by: OpenAI Codex:model-not-exposed
1 parent 392c100 commit 0d4c31a

2 files changed

Lines changed: 21 additions & 8 deletions

File tree

‎crates/vm/src/stdlib/_ctypes/function.rs‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,17 +1213,17 @@ fn restype_checker(
12131213
fn extract_call_info(zelf: &Py<PyCFuncPtr>, vm: &VirtualMachine) -> PyResult<CallInfo> {
12141214
// Clone before calling Python sequence methods, which can change argtypes.
12151215
let argtypes = zelf.argtypes.read().clone();
1216-
// Get argtypes - first from instance, then from type's _argtypes_
1216+
// Get argtypes from the instance, then cached class metadata.
12171217
let explicit_arg_types: Option<Vec<PyTypeRef>> = if let Some(argtypes_obj) = argtypes.as_ref() {
12181218
if !vm.is_none(argtypes_obj) {
12191219
Some(extract_arg_types(argtypes_obj, vm)?)
12201220
} else {
12211221
None // argtypes is None -> use ConvParam
12221222
}
12231223
} else if let Some(class_argtypes) = zelf
1224-
.as_object()
12251224
.class()
1226-
.get_attr(vm.ctx.intern_str("_argtypes_"))
1225+
.stg_info_opt()
1226+
.and_then(|info| info.argtypes.clone())
12271227
&& !vm.is_none(&class_argtypes)
12281228
{
12291229
Some(extract_arg_types(&class_argtypes, vm)?)
@@ -2047,12 +2047,12 @@ impl Py<PyCFuncPtr> {
20472047

20482048
// argtypes getter/setter
20492049
#[pygetset]
2050-
fn argtypes(zelf: &Self, vm: &VirtualMachine) -> Option<PyObjectRef> {
2050+
fn argtypes(zelf: &Self, _vm: &VirtualMachine) -> Option<PyObjectRef> {
20512051
let argtypes = zelf.argtypes.read().clone();
20522052
argtypes.or_else(|| {
2053-
zelf.as_object()
2054-
.class()
2055-
.get_attr(vm.ctx.intern_str("_argtypes_"))
2053+
zelf.class()
2054+
.stg_info_opt()
2055+
.and_then(|info| info.argtypes.clone())
20562056
})
20572057
}
20582058

‎extra_tests/snippets/stdlib_ctypes.py‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1299,7 +1299,12 @@ class Child(Base):
12991299
Child(lambda: 37)
13001300
Child._argtypes_ = ()
13011301

1302-
def test_callback_uses_cached_argtypes_metadata(self):
1302+
callback = ctypes.CFUNCTYPE(c_int)(lambda: 37)
1303+
ordinary = Child(ctypes.cast(callback, ctypes.c_void_p).value)
1304+
self.assertIsNone(ordinary.argtypes)
1305+
self.assertEqual(ordinary(), 37)
1306+
1307+
def test_function_uses_cached_argtypes_metadata(self):
13031308
class Prototype(ctypes._CFuncPtr):
13041309
_flags_ = ctypes._FUNCFLAG_CDECL
13051310
_argtypes_ = ()
@@ -1309,6 +1314,14 @@ class Prototype(ctypes._CFuncPtr):
13091314
function = Prototype(lambda: 37)
13101315
self.assertEqual(function.argtypes, ())
13111316
self.assertEqual(function(), 37)
1317+
ordinary = Prototype(ctypes.cast(function, ctypes.c_void_p).value)
1318+
self.assertEqual(ordinary.argtypes, ())
1319+
self.assertEqual(ordinary(), 37)
1320+
ordinary.argtypes = (c_int,)
1321+
self.assertEqual(ordinary.argtypes, (c_int,))
1322+
ordinary.argtypes = None
1323+
self.assertEqual(ordinary.argtypes, ())
1324+
self.assertEqual(ordinary(), 37)
13121325

13131326
@_unittest.skipUnless(
13141327
_sys.implementation.name == "rustpython",

0 commit comments

Comments
 (0)