Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

_ctypes pt. 4 #5582

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions 23 Lib/ctypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \
FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR

# TODO: RUSTPYTHON remove this
from _ctypes import _non_existing_function

# WINOLEAPI -> HRESULT
# WINOLEAPI_(type)
#
Expand Down Expand Up @@ -296,7 +299,9 @@ def create_unicode_buffer(init, size=None):
return buf
elif isinstance(init, int):
_sys.audit("ctypes.create_unicode_buffer", None, init)
buftype = c_wchar * init
# XXX: RustPython
youknowone marked this conversation as resolved.
Show resolved Hide resolved
# buftype = c_wchar * init
buftype = c_wchar.__mul__(init)
arihant2math marked this conversation as resolved.
Show resolved Hide resolved
buf = buftype()
return buf
raise TypeError(init)
Expand Down Expand Up @@ -495,14 +500,15 @@ def WinError(code=None, descr=None):
c_ssize_t = c_longlong

# functions

from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, _cast_addr

## void *memmove(void *, const void *, size_t);
memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr)
# TODO: RUSTPYTHON
youknowone marked this conversation as resolved.
Show resolved Hide resolved
# memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr)

## void *memset(void *, int, size_t)
memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr)
# TODO: RUSTPYTHON
youknowone marked this conversation as resolved.
Show resolved Hide resolved
# memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr)

def PYFUNCTYPE(restype, *argtypes):
class CFunctionType(_CFuncPtr):
Expand All @@ -511,11 +517,13 @@ class CFunctionType(_CFuncPtr):
_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI
return CFunctionType

_cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
# TODO: RUSTPYTHON
youknowone marked this conversation as resolved.
Show resolved Hide resolved
# _cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr)
def cast(obj, typ):
return _cast(obj, obj, typ)

_string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
# TODO: RUSTPYTHON
youknowone marked this conversation as resolved.
Show resolved Hide resolved
# _string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
def string_at(ptr, size=-1):
"""string_at(addr[, size]) -> string

Expand All @@ -527,7 +535,8 @@ def string_at(ptr, size=-1):
except ImportError:
pass
else:
_wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr)
# TODO: RUSTPYTHON
youknowone marked this conversation as resolved.
Show resolved Hide resolved
# _wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr)
def wstring_at(ptr, size=-1):
"""wstring_at(addr[, size]) -> string

Expand Down
63 changes: 60 additions & 3 deletions 63 extra_tests/snippets/builtins_ctypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@ def create_string_buffer(init, size=None):
if size is None:
size = len(init)+1
_sys.audit("ctypes.create_string_buffer", init, size)
buftype = c_char * size
buftype = c_char.__mul__(size)
print(type(c_char.__mul__(size)))
# buftype = c_char * size
buf = buftype()
buf.value = init
return buf
elif isinstance(init, int):
_sys.audit("ctypes.create_string_buffer", None, init)
buftype = c_char * init
buftype = c_char.__mul__(init)
# buftype = c_char * init
buf = buftype()
return buf
raise TypeError(init)
Expand Down Expand Up @@ -260,8 +263,62 @@ def LoadLibrary(self, name):

cdll = LibraryLoader(CDLL)

test_byte_array = create_string_buffer(b"Hello, World!\n")
assert test_byte_array._length_ == 15

if _os.name == "posix" or _sys.platform == "darwin":
pass
else:
import os

libc = cdll.msvcrt
print("rand", libc.rand())
libc.rand()
i = c_int(1)
print("start srand")
print(libc.srand(i))
print(test_byte_array)
print(test_byte_array._type_)
# print("start printf")
# libc.printf(test_byte_array)

# windows pip support

def get_win_folder_via_ctypes(csidl_name: str) -> str:
"""Get folder with ctypes."""
# There is no 'CSIDL_DOWNLOADS'.
# Use 'CSIDL_PROFILE' (40) and append the default folder 'Downloads' instead.
# https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid

import ctypes # noqa: PLC0415

csidl_const = {
"CSIDL_APPDATA": 26,
"CSIDL_COMMON_APPDATA": 35,
"CSIDL_LOCAL_APPDATA": 28,
"CSIDL_PERSONAL": 5,
"CSIDL_MYPICTURES": 39,
"CSIDL_MYVIDEO": 14,
"CSIDL_MYMUSIC": 13,
"CSIDL_DOWNLOADS": 40,
"CSIDL_DESKTOPDIRECTORY": 16,
}.get(csidl_name)
if csidl_const is None:
msg = f"Unknown CSIDL name: {csidl_name}"
raise ValueError(msg)

buf = ctypes.create_unicode_buffer(1024)
windll = getattr(ctypes, "windll") # noqa: B009 # using getattr to avoid false positive with mypy type checker
windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)

# Downgrade to short path name if it has high-bit chars.
if any(ord(c) > 255 for c in buf): # noqa: PLR2004
buf2 = ctypes.create_unicode_buffer(1024)
if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
buf = buf2

if csidl_name == "CSIDL_DOWNLOADS":
return os.path.join(buf.value, "Downloads") # noqa: PTH118

return buf.value

# print(get_win_folder_via_ctypes("CSIDL_DOWNLOADS"))
68 changes: 65 additions & 3 deletions 68 vm/src/stdlib/ctypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub fn extend_module_nodes(vm: &VirtualMachine, module: &Py<PyModule>) {
extend_module!(vm, module, {
"_CData" => PyCData::make_class(ctx),
"_SimpleCData" => PyCSimple::make_class(ctx),
"ArrayType" => array::PyCArrayType::make_class(ctx),
"Array" => array::PyCArray::make_class(ctx),
"CFuncPtr" => function::PyCFuncPtr::make_class(ctx),
"_Pointer" => pointer::PyCPointer::make_class(ctx),
Expand All @@ -37,7 +38,7 @@ pub(crate) mod _ctypes {
use super::base::PyCSimple;
use crate::builtins::PyTypeRef;
use crate::class::StaticType;
use crate::function::{Either, OptionalArg};
use crate::function::{Either, FuncArgs, OptionalArg};
use crate::stdlib::ctypes::library;
use crate::{AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine};
use crossbeam_utils::atomic::AtomicCell;
Expand Down Expand Up @@ -124,11 +125,12 @@ pub(crate) mod _ctypes {
"d" | "g" => mem::size_of::<c_double>(),
"?" | "B" => mem::size_of::<c_uchar>(),
"P" | "z" | "Z" => mem::size_of::<usize>(),
"O" => mem::size_of::<PyObjectRef>(),
_ => unreachable!(),
}
}

const SIMPLE_TYPE_CHARS: &str = "cbBhHiIlLdfguzZPqQ?";
const SIMPLE_TYPE_CHARS: &str = "cbBhHiIlLdfguzZPqQ?O";

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

#[pyfunction]
#[pyfunction(name = "pointer")]
pub fn pointer_fn(_inst: PyObjectRef) {}

#[pyfunction]
fn _pointer_type_cache() -> PyObjectRef {
todo!()
}

#[cfg(target_os = "windows")]
#[pyfunction(name = "_check_HRESULT")]
pub fn check_hresult(_self: PyObjectRef, hr: i32, _vm: &VirtualMachine) -> PyResult<i32> {
Expand All @@ -243,6 +250,24 @@ pub(crate) mod _ctypes {
}
}

#[pyfunction]
fn byref(_args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
// TODO: RUSTPYTHON
Err(vm.new_value_error("not implemented".to_string()))
}

#[pyfunction]
fn alignment(_args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
// TODO: RUSTPYTHON
Err(vm.new_value_error("not implemented".to_string()))
}

#[pyfunction]
fn resize(_args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
// TODO: RUSTPYTHON
Err(vm.new_value_error("not implemented".to_string()))
}

#[pyfunction]
fn get_errno() -> i32 {
errno::errno().0
Expand All @@ -252,4 +277,41 @@ pub(crate) mod _ctypes {
fn set_errno(value: i32) {
errno::set_errno(errno::Errno(value));
}

#[cfg(windows)]
#[pyfunction]
fn get_last_error() -> PyResult<u32> {
Ok(unsafe { windows_sys::Win32::Foundation::GetLastError() })
}

#[cfg(windows)]
#[pyfunction]
fn set_last_error(value: u32) -> PyResult<()> {
unsafe { windows_sys::Win32::Foundation::SetLastError(value) };
Ok(())
}

#[pyattr]
fn _memmove_addr(_vm: &VirtualMachine) -> usize {
let f = libc::memmove;
f as usize
}

#[pyattr]
fn _memset_addr(_vm: &VirtualMachine) -> usize {
let f = libc::memset;
f as usize
}

#[pyattr]
fn _string_at_addr(_vm: &VirtualMachine) -> usize {
let f = libc::strnlen;
f as usize
}

#[pyattr]
fn _cast_addr(_vm: &VirtualMachine) -> usize {
// TODO: RUSTPYTHON
0
}
}
110 changes: 106 additions & 4 deletions 110 vm/src/stdlib/ctypes/array.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,107 @@
#[pyclass(name = "Array", module = "_ctypes")]
pub struct PyCArray {}
use crate::builtins::PyBytes;
use crate::types::Callable;
use crate::{Py, PyObjectRef, PyPayload};
use crate::{PyResult, VirtualMachine, builtins::PyTypeRef, types::Constructor};
use crossbeam_utils::atomic::AtomicCell;
use rustpython_common::lock::PyRwLock;
use rustpython_vm::stdlib::ctypes::base::PyCSimple;

#[pyclass(flags(BASETYPE, IMMUTABLETYPE))]
impl PyCArray {}
// TODO: make it metaclass
#[pyclass(name = "ArrayType", module = "_ctypes")]
#[derive(PyPayload)]
pub struct PyCArrayType {
pub(super) inner: PyCArray,
}

impl std::fmt::Debug for PyCArrayType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PyCArrayType")
.field("inner", &self.inner)
.finish()
}
}

impl Callable for PyCArrayType {
type Args = ();
fn call(zelf: &Py<Self>, _args: Self::Args, vm: &VirtualMachine) -> PyResult {
Ok(PyCArray {
typ: PyRwLock::new(zelf.inner.typ.read().clone()),
length: AtomicCell::new(zelf.inner.length.load()),
value: PyRwLock::new(zelf.inner.value.read().clone()),
}
.into_pyobject(vm))
}
}

impl Constructor for PyCArrayType {
type Args = PyObjectRef;

fn py_new(_cls: PyTypeRef, _args: Self::Args, _vm: &VirtualMachine) -> PyResult {
unreachable!()
}
}

#[pyclass(flags(IMMUTABLETYPE), with(Callable, Constructor))]
impl PyCArrayType {}

#[pyclass(name = "Array", base = "PyCSimple", module = "_ctypes")]
#[derive(PyPayload)]
pub struct PyCArray {
pub(super) typ: PyRwLock<PyTypeRef>,
pub(super) length: AtomicCell<usize>,
pub(super) value: PyRwLock<PyObjectRef>,
}

impl std::fmt::Debug for PyCArray {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PyCArray")
.field("typ", &self.typ)
.field("length", &self.length)
.finish()
}
}

impl Constructor for PyCArray {
type Args = (PyTypeRef, usize);

fn py_new(_cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
Ok(Self {
typ: PyRwLock::new(args.0),
length: AtomicCell::new(args.1),
value: PyRwLock::new(vm.ctx.none()),
}
.into_pyobject(vm))
}
}

#[pyclass(flags(BASETYPE, IMMUTABLETYPE), with(Constructor))]
impl PyCArray {
#[pygetset(name = "_type_")]
fn typ(&self) -> PyTypeRef {
self.typ.read().clone()
}

#[pygetset(name = "_length_")]
fn length(&self) -> usize {
self.length.load()
}

#[pygetset]
fn value(&self) -> PyObjectRef {
self.value.read().clone()
}

#[pygetset(setter)]
fn set_value(&self, value: PyObjectRef) {
*self.value.write() = value;
}
}

impl PyCArray {
pub fn to_arg(&self, _vm: &VirtualMachine) -> PyResult<libffi::middle::Arg> {
let value = self.value.read();
let py_bytes = value.payload::<PyBytes>().unwrap();
let bytes = py_bytes.as_ref().to_vec();
Ok(libffi::middle::Arg::new(&bytes))
}
}
Loading
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.