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

Initial _ctypes implementation #5519

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 3 commits into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 3 additions & 2 deletions 5 Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

105 changes: 105 additions & 0 deletions 105 extra_tests/snippets/builtins_ctypes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from _ctypes import sizeof
from _ctypes import _SimpleCData
from struct import calcsize as _calcsize

def _check_size(typ, typecode=None):
# Check if sizeof(ctypes_type) against struct.calcsize. This
# should protect somewhat against a misconfigured libffi.
from struct import calcsize
if typecode is None:
# Most _type_ codes are the same as used in struct
typecode = typ._type_
actual, required = sizeof(typ), calcsize(typecode)
if actual != required:
raise SystemError("sizeof(%s) wrong: %d instead of %d" % \
(typ, actual, required))

class c_short(_SimpleCData):
_type_ = "h"
_check_size(c_short)

class c_ushort(_SimpleCData):
_type_ = "H"
_check_size(c_ushort)

class c_long(_SimpleCData):
_type_ = "l"
_check_size(c_long)

class c_ulong(_SimpleCData):
_type_ = "L"
_check_size(c_ulong)

if _calcsize("i") == _calcsize("l"):
# if int and long have the same size, make c_int an alias for c_long
c_int = c_long
c_uint = c_ulong
else:
class c_int(_SimpleCData):
_type_ = "i"
_check_size(c_int)

class c_uint(_SimpleCData):
_type_ = "I"
_check_size(c_uint)

class c_float(_SimpleCData):
_type_ = "f"
_check_size(c_float)

class c_double(_SimpleCData):
_type_ = "d"
_check_size(c_double)

class c_longdouble(_SimpleCData):
_type_ = "g"
if sizeof(c_longdouble) == sizeof(c_double):
c_longdouble = c_double

if _calcsize("l") == _calcsize("q"):
# if long and long long have the same size, make c_longlong an alias for c_long
c_longlong = c_long
c_ulonglong = c_ulong
else:
class c_longlong(_SimpleCData):
_type_ = "q"
_check_size(c_longlong)

class c_ulonglong(_SimpleCData):
_type_ = "Q"
## def from_param(cls, val):
## return ('d', float(val), val)
## from_param = classmethod(from_param)
_check_size(c_ulonglong)

class c_ubyte(_SimpleCData):
_type_ = "B"
c_ubyte.__ctype_le__ = c_ubyte.__ctype_be__ = c_ubyte
# backward compatibility:
##c_uchar = c_ubyte
_check_size(c_ubyte)

class c_byte(_SimpleCData):
_type_ = "b"
c_byte.__ctype_le__ = c_byte.__ctype_be__ = c_byte
_check_size(c_byte)

class c_char(_SimpleCData):
_type_ = "c"
c_char.__ctype_le__ = c_char.__ctype_be__ = c_char
_check_size(c_char)

class c_char_p(_SimpleCData):
_type_ = "z"
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, c_void_p.from_buffer(self).value)
_check_size(c_char_p, "P")

class c_void_p(_SimpleCData):
_type_ = "P"
c_voidp = c_void_p # backwards compatibility (to a bug)
_check_size(c_void_p)

class c_bool(_SimpleCData):
_type_ = "?"
_check_size(c_bool)
3 changes: 2 additions & 1 deletion 3 vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,15 @@ uname = "0.1.1"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
rustyline = { workspace = true }
which = "6"
errno = "0.3"
widestring = { workspace = true }

[target.'cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))'.dependencies]
num_cpus = "1.13.1"

[target.'cfg(windows)'.dependencies]
junction = { workspace = true }
schannel = { workspace = true }
widestring = { workspace = true }
winreg = "0.52"

[target.'cfg(windows)'.dependencies.windows]
Expand Down
107 changes: 107 additions & 0 deletions 107 vm/src/stdlib/ctypes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
pub(crate) mod base;

use crate::builtins::PyModule;
use crate::{PyRef, VirtualMachine};

pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
let module = _ctypes::make_module(vm);
base::extend_module_nodes(vm, &module);
module
}

#[pymodule]
pub(crate) mod _ctypes {
use super::base::PyCSimple;
use crate::builtins::PyTypeRef;
use crate::class::StaticType;
use crate::function::Either;
use crate::{AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine};
use crossbeam_utils::atomic::AtomicCell;
use std::ffi::{
c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong,
c_ulonglong,
};
use std::mem;
use widestring::WideChar;

pub fn get_size(ty: &str) -> usize {
match ty {
"u" => mem::size_of::<WideChar>(),
"c" | "b" => mem::size_of::<c_schar>(),
"h" => mem::size_of::<c_short>(),
"H" => mem::size_of::<c_short>(),
"i" => mem::size_of::<c_int>(),
"I" => mem::size_of::<c_uint>(),
"l" => mem::size_of::<c_long>(),
"q" => mem::size_of::<c_longlong>(),
"L" => mem::size_of::<c_ulong>(),
"Q" => mem::size_of::<c_ulonglong>(),
"f" => mem::size_of::<c_float>(),
"d" | "g" => mem::size_of::<c_double>(),
"?" | "B" => mem::size_of::<c_uchar>(),
"P" | "z" | "Z" => mem::size_of::<usize>(),
_ => unreachable!(),
}
}

const SIMPLE_TYPE_CHARS: &str = "cbBhHiIlLdfguzZPqQ?";

pub fn new_simple_type(
cls: Either<&PyObjectRef, &PyTypeRef>,
vm: &VirtualMachine,
) -> PyResult<PyCSimple> {
let cls = match cls {
Either::A(obj) => obj,
Either::B(typ) => typ.as_object(),
};

if let Ok(_type_) = cls.get_attr("_type_", vm) {
if _type_.is_instance((&vm.ctx.types.str_type).as_ref(), vm)? {
let tp_str = _type_.str(vm)?.to_string();

if tp_str.len() != 1 {
Err(vm.new_value_error(
format!("class must define a '_type_' attribute which must be a string of length 1, str: {tp_str}"),
))
} else if !SIMPLE_TYPE_CHARS.contains(tp_str.as_str()) {
Err(vm.new_attribute_error(format!("class must define a '_type_' attribute which must be\n a single character string containing one of {SIMPLE_TYPE_CHARS}, currently it is {tp_str}.")))
} else {
Ok(PyCSimple {
_type_: tp_str,
_value: AtomicCell::new(vm.ctx.none()),
})
}
} else {
Err(vm.new_type_error("class must define a '_type_' string attribute".to_string()))
}
} else {
Err(vm.new_attribute_error("class must define a '_type_' attribute".to_string()))
}
}

#[pyfunction(name = "sizeof")]
pub fn size_of(tp: Either<PyTypeRef, PyObjectRef>, vm: &VirtualMachine) -> PyResult<usize> {
match tp {
Either::A(type_) if type_.fast_issubclass(PyCSimple::static_type()) => {
let zelf = new_simple_type(Either::B(&type_), vm)?;
Ok(get_size(zelf._type_.as_str()))
}
Either::B(obj) if obj.has_attr("size_of_instances", vm)? => {
let size_of_method = obj.get_attr("size_of_instances", vm)?;
let size_of_return = size_of_method.call(vec![], vm)?;
Ok(usize::try_from_object(vm, size_of_return)?)
}
_ => Err(vm.new_type_error("this type has no size".to_string())),
}
}

#[pyfunction]
fn get_errno() -> i32 {
errno::errno().0
}

#[pyfunction]
fn set_errno(value: i32) {
errno::set_errno(errno::Errno(value));
}
}
Loading
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.