X Tutup
Skip to content

Commit 38d54fa

Browse files
committed
impl more ctypes
- Add __pointer_type__ getter/setter and StgInfo cache - Add kept_refs (HashMap-keyed) for c_char_p lifetime - Simplify ensure_z_null_terminated to 2-value return - Add RawMemoryBuffer for zero-copy memoryview_at_addr - Add bitfield support in PyCField with validation - Add CArgObject offset handling in extract_ptr_from_arg - Add deprecation warning for _pack_ without _layout_ - Fix comparison TypeError message format - Fix unsupported binop error message format - Mark failing CI tests as expectedFailure/skip
1 parent 36a53dd commit 38d54fa

File tree

15 files changed

+859
-104
lines changed

15 files changed

+859
-104
lines changed

Lib/test/test_ctypes/test_dlerror.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ class TestNullDlsym(unittest.TestCase):
5555
this 'dlsym returned NULL -> throw Error' rule.
5656
"""
5757

58+
# TODO: RUSTPYTHON
59+
@unittest.expectedFailure
5860
def test_null_dlsym(self):
5961
import subprocess
6062
import tempfile

Lib/test/test_ctypes/test_dllist.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
)
2727
class ListSharedLibraries(unittest.TestCase):
2828

29+
# TODO: RUSTPYTHON
30+
@unittest.skipIf(not APPLE, "TODO: RUSTPYTHON")
2931
def test_lists_system(self):
3032
dlls = ctypes.util.dllist()
3133

@@ -34,6 +36,8 @@ def test_lists_system(self):
3436
any(lib in dll for dll in dlls for lib in KNOWN_LIBRARIES), f"loaded={dlls}"
3537
)
3638

39+
# TODO: RUSTPYTHON
40+
@unittest.expectedFailure
3741
def test_lists_updates(self):
3842
dlls = ctypes.util.dllist()
3943

Lib/test/test_ctypes/test_python_api.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88

99
class PythonAPITestCase(unittest.TestCase):
10+
# TODO: RUSTPYTHON - requires pythonapi (Python C API)
11+
@unittest.expectedFailure
1012
def test_PyBytes_FromStringAndSize(self):
1113
PyBytes_FromStringAndSize = pythonapi.PyBytes_FromStringAndSize
1214

@@ -57,6 +59,8 @@ def test_PyObj_FromPtr(self):
5759
del pyobj
5860
self.assertEqual(sys.getrefcount(s), ref)
5961

62+
# TODO: RUSTPYTHON - requires pythonapi (Python C API)
63+
@unittest.expectedFailure
6064
def test_PyOS_snprintf(self):
6165
PyOS_snprintf = pythonapi.PyOS_snprintf
6266
PyOS_snprintf.argtypes = POINTER(c_char), c_size_t, c_char_p

Lib/test/test_ctypes/test_values.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,17 @@ def test_undefined(self):
3939
class PythonValuesTestCase(unittest.TestCase):
4040
"""This test only works when python itself is a dll/shared library"""
4141

42+
# TODO: RUSTPYTHON - requires pythonapi (Python C API)
43+
@unittest.expectedFailure
4244
def test_optimizeflag(self):
4345
# This test accesses the Py_OptimizeFlag integer, which is
4446
# exported by the Python dll and should match the sys.flags value
4547

4648
opt = c_int.in_dll(pythonapi, "Py_OptimizeFlag").value
4749
self.assertEqual(opt, sys.flags.optimize)
4850

51+
# TODO: RUSTPYTHON - requires pythonapi (Python C API)
52+
@unittest.expectedFailure
4953
@thread_unsafe('overrides frozen modules')
5054
def test_frozentable(self):
5155
# Python exports a PyImport_FrozenModules symbol. This is a

Lib/test/test_ctypes/test_win32.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
1515
class FunctionCallTestCase(unittest.TestCase):
16+
# TODO: RUSTPYTHON: SEH not implemented, crashes with STATUS_ACCESS_VIOLATION
17+
@unittest.skip("TODO: RUSTPYTHON")
1618
@unittest.skipUnless('MSC' in sys.version, "SEH only supported by MSC")
1719
@unittest.skipIf(sys.executable.lower().endswith('_d.exe'),
1820
"SEH not enabled in debug builds")

crates/vm/src/protocol/object.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,12 @@ impl PyObject {
322322
match op {
323323
PyComparisonOp::Eq => Ok(Either::B(self.is(&other))),
324324
PyComparisonOp::Ne => Ok(Either::B(!self.is(&other))),
325-
_ => Err(vm.new_unsupported_bin_op_error(self, other, op.operator_token())),
325+
_ => Err(vm.new_type_error(format!(
326+
"'{}' not supported between instances of '{}' and '{}'",
327+
op.operator_token(),
328+
self.class().name(),
329+
other.class().name()
330+
))),
326331
}
327332
}
328333
#[inline(always)]

crates/vm/src/stdlib/ctypes.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,26 @@ impl Py<PyType> {
7070
}
7171

7272
impl PyType {
73-
/// Check if StgInfo is already initialized - prevent double initialization
73+
/// Check if StgInfo is already initialized.
74+
/// Raises SystemError if already initialized.
7475
pub(crate) fn check_not_initialized(&self, vm: &VirtualMachine) -> PyResult<()> {
7576
if let Some(stg_info) = self.get_type_data::<StgInfo>()
7677
&& stg_info.initialized
7778
{
7879
return Err(vm.new_exception_msg(
7980
vm.ctx.exceptions.system_error.to_owned(),
80-
format!("StgInfo of '{}' is already initialized.", self.name()),
81+
format!("class \"{}\" already initialized", self.name()),
8182
));
8283
}
8384
Ok(())
8485
}
86+
87+
/// Check if StgInfo is already initialized, returning true if so.
88+
/// Unlike check_not_initialized, does not raise an error.
89+
pub(crate) fn is_initialized(&self) -> bool {
90+
self.get_type_data::<StgInfo>()
91+
.is_some_and(|stg_info| stg_info.initialized)
92+
}
8593
}
8694

8795
// Dynamic type check helpers for PyCData
@@ -988,6 +996,11 @@ pub(crate) mod _ctypes {
988996
super::function::INTERNAL_CAST_ADDR
989997
}
990998

999+
#[pyattr]
1000+
fn _memoryview_at_addr(_vm: &VirtualMachine) -> usize {
1001+
super::function::INTERNAL_MEMORYVIEW_AT_ADDR
1002+
}
1003+
9911004
#[pyfunction]
9921005
fn _cast(
9931006
obj: PyObjectRef,
@@ -1293,6 +1306,7 @@ pub(crate) mod _ctypes {
12931306
structure::PyCStructType::make_class(ctx);
12941307
union::PyCUnionType::make_class(ctx);
12951308
function::PyCFuncPtrType::make_class(ctx);
1309+
function::RawMemoryBuffer::make_class(ctx);
12961310

12971311
extend_module!(vm, module, {
12981312
"_CData" => PyCData::make_class(ctx),

crates/vm/src/stdlib/ctypes/array.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,16 @@ impl Initializer for PyCArrayType {
301301

302302
#[pyclass(flags(IMMUTABLETYPE), with(Initializer, AsNumber))]
303303
impl PyCArrayType {
304+
#[pygetset(name = "__pointer_type__")]
305+
fn pointer_type(zelf: PyTypeRef, vm: &VirtualMachine) -> PyResult {
306+
super::base::pointer_type_get(&zelf, vm)
307+
}
308+
309+
#[pygetset(name = "__pointer_type__", setter)]
310+
fn set_pointer_type(zelf: PyTypeRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
311+
super::base::pointer_type_set(&zelf, value, vm)
312+
}
313+
304314
#[pymethod]
305315
fn from_param(zelf: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult {
306316
// zelf is the array type class that from_param was called on
@@ -777,8 +787,9 @@ impl PyCArray {
777787
let (ptr_val, converted) = if value.is(&vm.ctx.none) {
778788
(0usize, None)
779789
} else if let Some(bytes) = value.downcast_ref::<PyBytes>() {
780-
let (c, ptr) = super::base::ensure_z_null_terminated(bytes, vm);
781-
(ptr, Some(c))
790+
let (kept_alive, ptr) = super::base::ensure_z_null_terminated(bytes, vm);
791+
zelf.0.keep_alive(index, kept_alive);
792+
(ptr, Some(value.to_owned()))
782793
} else if let Ok(int_val) = value.try_index(vm) {
783794
(int_val.as_bigint().to_usize().unwrap_or(0), None)
784795
} else {

0 commit comments

Comments
 (0)
X Tutup