Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 01d470f

Browse files
authored
_ctypes pt. 4 (#5582)
* correct error type when symbol is not found * restype get/set * base of PyCSimple for PyCArray * PyCSimple::to_arg * par down ctypes * nt._LOAD_LIBRARY_SEARCH_DEFAULT_DIRS * arguments for ctypes function * force failure to import ctypes
1 parent 9779de9 commit 01d470f

File tree

7 files changed

+368
-29
lines changed

7 files changed

+368
-29
lines changed

Lib/ctypes/__init__.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \
3737
FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR
3838

39+
# TODO: RUSTPYTHON remove this
40+
from _ctypes import _non_existing_function
41+
3942
# WINOLEAPI -> HRESULT
4043
# WINOLEAPI_(type)
4144
#
@@ -296,7 +299,9 @@ def create_unicode_buffer(init, size=None):
296299
return buf
297300
elif isinstance(init, int):
298301
_sys.audit("ctypes.create_unicode_buffer", None, init)
299-
buftype = c_wchar * init
302+
# XXX: RUSTPYTHON
303+
# buftype = c_wchar * init
304+
buftype = c_wchar.__mul__(init)
300305
buf = buftype()
301306
return buf
302307
raise TypeError(init)
@@ -495,14 +500,15 @@ def WinError(code=None, descr=None):
495500
c_ssize_t = c_longlong
496501

497502
# functions
498-
499503
from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, _cast_addr
500504

501505
## void *memmove(void *, const void *, size_t);
502-
memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr)
506+
# XXX: RUSTPYTHON
507+
# memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr)
503508

504509
## void *memset(void *, int, size_t)
505-
memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr)
510+
# XXX: RUSTPYTHON
511+
# memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr)
506512

507513
def PYFUNCTYPE(restype, *argtypes):
508514
class CFunctionType(_CFuncPtr):
@@ -511,11 +517,13 @@ class CFunctionType(_CFuncPtr):
511517
_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
512518
return CFunctionType
513519

514-
_cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
520+
# XXX: RUSTPYTHON
521+
# _cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
515522
def cast(obj, typ):
516523
return _cast(obj, obj, typ)
517524

518-
_string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
525+
# XXX: RUSTPYTHON
526+
# _string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
519527
def string_at(ptr, size=-1):
520528
"""string_at(addr[, size]) -> string
521529
@@ -527,7 +535,8 @@ def string_at(ptr, size=-1):
527535
except ImportError:
528536
pass
529537
else:
530-
_wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr)
538+
# XXX: RUSTPYTHON
539+
# _wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr)
531540
def wstring_at(ptr, size=-1):
532541
"""wstring_at(addr[, size]) -> string
533542

extra_tests/snippets/builtins_ctypes.py

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,16 @@ def create_string_buffer(init, size=None):
3333
if size is None:
3434
size = len(init)+1
3535
_sys.audit("ctypes.create_string_buffer", init, size)
36-
buftype = c_char * size
36+
buftype = c_char.__mul__(size)
37+
print(type(c_char.__mul__(size)))
38+
# buftype = c_char * size
3739
buf = buftype()
3840
buf.value = init
3941
return buf
4042
elif isinstance(init, int):
4143
_sys.audit("ctypes.create_string_buffer", None, init)
42-
buftype = c_char * init
44+
buftype = c_char.__mul__(init)
45+
# buftype = c_char * init
4346
buf = buftype()
4447
return buf
4548
raise TypeError(init)
@@ -260,8 +263,62 @@ def LoadLibrary(self, name):
260263

261264
cdll = LibraryLoader(CDLL)
262265

266+
test_byte_array = create_string_buffer(b"Hello, World!\n")
267+
assert test_byte_array._length_ == 15
268+
263269
if _os.name == "posix" or _sys.platform == "darwin":
264270
pass
265271
else:
272+
import os
273+
266274
libc = cdll.msvcrt
267-
print("rand", libc.rand())
275+
libc.rand()
276+
i = c_int(1)
277+
print("start srand")
278+
print(libc.srand(i))
279+
print(test_byte_array)
280+
print(test_byte_array._type_)
281+
# print("start printf")
282+
# libc.printf(test_byte_array)
283+
284+
# windows pip support
285+
286+
def get_win_folder_via_ctypes(csidl_name: str) -> str:
287+
"""Get folder with ctypes."""
288+
# There is no 'CSIDL_DOWNLOADS'.
289+
# Use 'CSIDL_PROFILE' (40) and append the default folder 'Downloads' instead.
290+
# https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
291+
292+
import ctypes # noqa: PLC0415
293+
294+
csidl_const = {
295+
"CSIDL_APPDATA": 26,
296+
"CSIDL_COMMON_APPDATA": 35,
297+
"CSIDL_LOCAL_APPDATA": 28,
298+
"CSIDL_PERSONAL": 5,
299+
"CSIDL_MYPICTURES": 39,
300+
"CSIDL_MYVIDEO": 14,
301+
"CSIDL_MYMUSIC": 13,
302+
"CSIDL_DOWNLOADS": 40,
303+
"CSIDL_DESKTOPDIRECTORY": 16,
304+
}.get(csidl_name)
305+
if csidl_const is None:
306+
msg = f"Unknown CSIDL name: {csidl_name}"
307+
raise ValueError(msg)
308+
309+
buf = ctypes.create_unicode_buffer(1024)
310+
windll = getattr(ctypes, "windll") # noqa: B009 # using getattr to avoid false positive with mypy type checker
311+
windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
312+
313+
# Downgrade to short path name if it has high-bit chars.
314+
if any(ord(c) > 255 for c in buf): # noqa: PLR2004
315+
buf2 = ctypes.create_unicode_buffer(1024)
316+
if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
317+
buf = buf2
318+
319+
if csidl_name == "CSIDL_DOWNLOADS":
320+
return os.path.join(buf.value, "Downloads") # noqa: PTH118
321+
322+
return buf.value
323+
324+
# print(get_win_folder_via_ctypes("CSIDL_DOWNLOADS"))

vm/src/stdlib/ctypes.rs

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub fn extend_module_nodes(vm: &VirtualMachine, module: &Py<PyModule>) {
1717
extend_module!(vm, module, {
1818
"_CData" => PyCData::make_class(ctx),
1919
"_SimpleCData" => PyCSimple::make_class(ctx),
20+
"ArrayType" => array::PyCArrayType::make_class(ctx),
2021
"Array" => array::PyCArray::make_class(ctx),
2122
"CFuncPtr" => function::PyCFuncPtr::make_class(ctx),
2223
"_Pointer" => pointer::PyCPointer::make_class(ctx),
@@ -37,7 +38,7 @@ pub(crate) mod _ctypes {
3738
use super::base::PyCSimple;
3839
use crate::builtins::PyTypeRef;
3940
use crate::class::StaticType;
40-
use crate::function::{Either, OptionalArg};
41+
use crate::function::{Either, FuncArgs, OptionalArg};
4142
use crate::stdlib::ctypes::library;
4243
use crate::{AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine};
4344
use crossbeam_utils::atomic::AtomicCell;
@@ -124,11 +125,12 @@ pub(crate) mod _ctypes {
124125
"d" | "g" => mem::size_of::<c_double>(),
125126
"?" | "B" => mem::size_of::<c_uchar>(),
126127
"P" | "z" | "Z" => mem::size_of::<usize>(),
128+
"O" => mem::size_of::<PyObjectRef>(),
127129
_ => unreachable!(),
128130
}
129131
}
130132

131-
const SIMPLE_TYPE_CHARS: &str = "cbBhHiIlLdfguzZPqQ?";
133+
const SIMPLE_TYPE_CHARS: &str = "cbBhHiIlLdfguzZPqQ?O";
132134

133135
pub fn new_simple_type(
134136
cls: Either<&PyObjectRef, &PyTypeRef>,
@@ -218,9 +220,14 @@ pub(crate) mod _ctypes {
218220
#[pyfunction(name = "POINTER")]
219221
pub fn pointer(_cls: PyTypeRef) {}
220222

221-
#[pyfunction]
223+
#[pyfunction(name = "pointer")]
222224
pub fn pointer_fn(_inst: PyObjectRef) {}
223225

226+
#[pyfunction]
227+
fn _pointer_type_cache() -> PyObjectRef {
228+
todo!()
229+
}
230+
224231
#[cfg(target_os = "windows")]
225232
#[pyfunction(name = "_check_HRESULT")]
226233
pub fn check_hresult(_self: PyObjectRef, hr: i32, _vm: &VirtualMachine) -> PyResult<i32> {
@@ -243,6 +250,24 @@ pub(crate) mod _ctypes {
243250
}
244251
}
245252

253+
#[pyfunction]
254+
fn byref(_args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
255+
// TODO: RUSTPYTHON
256+
Err(vm.new_value_error("not implemented".to_string()))
257+
}
258+
259+
#[pyfunction]
260+
fn alignment(_args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
261+
// TODO: RUSTPYTHON
262+
Err(vm.new_value_error("not implemented".to_string()))
263+
}
264+
265+
#[pyfunction]
266+
fn resize(_args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
267+
// TODO: RUSTPYTHON
268+
Err(vm.new_value_error("not implemented".to_string()))
269+
}
270+
246271
#[pyfunction]
247272
fn get_errno() -> i32 {
248273
errno::errno().0
@@ -252,4 +277,41 @@ pub(crate) mod _ctypes {
252277
fn set_errno(value: i32) {
253278
errno::set_errno(errno::Errno(value));
254279
}
280+
281+
#[cfg(windows)]
282+
#[pyfunction]
283+
fn get_last_error() -> PyResult<u32> {
284+
Ok(unsafe { windows_sys::Win32::Foundation::GetLastError() })
285+
}
286+
287+
#[cfg(windows)]
288+
#[pyfunction]
289+
fn set_last_error(value: u32) -> PyResult<()> {
290+
unsafe { windows_sys::Win32::Foundation::SetLastError(value) };
291+
Ok(())
292+
}
293+
294+
#[pyattr]
295+
fn _memmove_addr(_vm: &VirtualMachine) -> usize {
296+
let f = libc::memmove;
297+
f as usize
298+
}
299+
300+
#[pyattr]
301+
fn _memset_addr(_vm: &VirtualMachine) -> usize {
302+
let f = libc::memset;
303+
f as usize
304+
}
305+
306+
#[pyattr]
307+
fn _string_at_addr(_vm: &VirtualMachine) -> usize {
308+
let f = libc::strnlen;
309+
f as usize
310+
}
311+
312+
#[pyattr]
313+
fn _cast_addr(_vm: &VirtualMachine) -> usize {
314+
// TODO: RUSTPYTHON
315+
0
316+
}
255317
}

vm/src/stdlib/ctypes/array.rs

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,107 @@
1-
#[pyclass(name = "Array", module = "_ctypes")]
2-
pub struct PyCArray {}
1+
use crate::builtins::PyBytes;
2+
use crate::types::Callable;
3+
use crate::{Py, PyObjectRef, PyPayload};
4+
use crate::{PyResult, VirtualMachine, builtins::PyTypeRef, types::Constructor};
5+
use crossbeam_utils::atomic::AtomicCell;
6+
use rustpython_common::lock::PyRwLock;
7+
use rustpython_vm::stdlib::ctypes::base::PyCSimple;
38

4-
#[pyclass(flags(BASETYPE, IMMUTABLETYPE))]
5-
impl PyCArray {}
9+
// TODO: make it metaclass
10+
#[pyclass(name = "ArrayType", module = "_ctypes")]
11+
#[derive(PyPayload)]
12+
pub struct PyCArrayType {
13+
pub(super) inner: PyCArray,
14+
}
15+
16+
impl std::fmt::Debug for PyCArrayType {
17+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18+
f.debug_struct("PyCArrayType")
19+
.field("inner", &self.inner)
20+
.finish()
21+
}
22+
}
23+
24+
impl Callable for PyCArrayType {
25+
type Args = ();
26+
fn call(zelf: &Py<Self>, _args: Self::Args, vm: &VirtualMachine) -> PyResult {
27+
Ok(PyCArray {
28+
typ: PyRwLock::new(zelf.inner.typ.read().clone()),
29+
length: AtomicCell::new(zelf.inner.length.load()),
30+
value: PyRwLock::new(zelf.inner.value.read().clone()),
31+
}
32+
.into_pyobject(vm))
33+
}
34+
}
35+
36+
impl Constructor for PyCArrayType {
37+
type Args = PyObjectRef;
38+
39+
fn py_new(_cls: PyTypeRef, _args: Self::Args, _vm: &VirtualMachine) -> PyResult {
40+
unreachable!()
41+
}
42+
}
43+
44+
#[pyclass(flags(IMMUTABLETYPE), with(Callable, Constructor))]
45+
impl PyCArrayType {}
46+
47+
#[pyclass(name = "Array", base = "PyCSimple", module = "_ctypes")]
48+
#[derive(PyPayload)]
49+
pub struct PyCArray {
50+
pub(super) typ: PyRwLock<PyTypeRef>,
51+
pub(super) length: AtomicCell<usize>,
52+
pub(super) value: PyRwLock<PyObjectRef>,
53+
}
54+
55+
impl std::fmt::Debug for PyCArray {
56+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57+
f.debug_struct("PyCArray")
58+
.field("typ", &self.typ)
59+
.field("length", &self.length)
60+
.finish()
61+
}
62+
}
63+
64+
impl Constructor for PyCArray {
65+
type Args = (PyTypeRef, usize);
66+
67+
fn py_new(_cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
68+
Ok(Self {
69+
typ: PyRwLock::new(args.0),
70+
length: AtomicCell::new(args.1),
71+
value: PyRwLock::new(vm.ctx.none()),
72+
}
73+
.into_pyobject(vm))
74+
}
75+
}
76+
77+
#[pyclass(flags(BASETYPE, IMMUTABLETYPE), with(Constructor))]
78+
impl PyCArray {
79+
#[pygetset(name = "_type_")]
80+
fn typ(&self) -> PyTypeRef {
81+
self.typ.read().clone()
82+
}
83+
84+
#[pygetset(name = "_length_")]
85+
fn length(&self) -> usize {
86+
self.length.load()
87+
}
88+
89+
#[pygetset]
90+
fn value(&self) -> PyObjectRef {
91+
self.value.read().clone()
92+
}
93+
94+
#[pygetset(setter)]
95+
fn set_value(&self, value: PyObjectRef) {
96+
*self.value.write() = value;
97+
}
98+
}
99+
100+
impl PyCArray {
101+
pub fn to_arg(&self, _vm: &VirtualMachine) -> PyResult<libffi::middle::Arg> {
102+
let value = self.value.read();
103+
let py_bytes = value.payload::<PyBytes>().unwrap();
104+
let bytes = py_bytes.as_ref().to_vec();
105+
Ok(libffi::middle::Arg::new(&bytes))
106+
}
107+
}

0 commit comments

Comments
 (0)