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 1 commit
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
Prev Previous commit
Next Next commit
Refactor of ctypes into module and more implementations
Adds sizeof and PyCSimple

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
  • Loading branch information
arihant2math committed Feb 14, 2025
commit fc7aa2361c935085c83800d8100ba596d6e56646
2 changes: 1 addition & 1 deletion 2 vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,14 @@ uname = "0.1.1"
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
103 changes: 88 additions & 15 deletions 103 vm/src/stdlib/ctypes.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,99 @@
pub(crate) use _ctypes::make_module;
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]
mod _ctypes {
use crate::{common::lock::PyRwLock, PyObjectRef};
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 struct RawBuffer {
#[allow(dead_code)]
pub inner: Box<[u8]>,
#[allow(dead_code)]
pub size: usize,
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!(),
}
}

#[pyattr]
#[pyclass(name = "_CData")]
pub struct PyCData {
_objects: AtomicCell<Vec<PyObjectRef>>,
_buffer: PyRwLock<RawBuffer>,
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()))
}
}

#[pyclass]
impl PyCData {}
#[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 {
Expand Down
161 changes: 161 additions & 0 deletions 161 vm/src/stdlib/ctypes/base.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use crate::builtins::{PyBytes, PyFloat, PyInt, PyModule, PyNone, PyStr};
use crate::class::PyClassImpl;
use crate::{Py, PyObjectRef, PyResult, TryFromObject, VirtualMachine};
use crossbeam_utils::atomic::AtomicCell;
use num_traits::ToPrimitive;
use rustpython_common::lock::PyRwLock;
use std::fmt::Debug;

#[allow(dead_code)]
fn set_primitive(_type_: &str, value: &PyObjectRef, vm: &VirtualMachine) -> PyResult {
match _type_ {
"c" => {
if value
.clone()
.downcast_exact::<PyBytes>(vm)
.is_ok_and(|v| v.len() == 1)
|| value
.clone()
.downcast_exact::<PyBytes>(vm)
.is_ok_and(|v| v.len() == 1)
|| value
.clone()
.downcast_exact::<PyInt>(vm)
.map_or(Ok(false), |v| {
let n = v.as_bigint().to_i64();
if let Some(n) = n {
Ok((0..=255).contains(&n))
} else {
Ok(false)
}
})?
{
Ok(value.clone())
} else {
Err(vm.new_type_error(
"one character bytes, bytearray or integer expected".to_string(),
))
}
}
"u" => {
if let Ok(b) = value.str(vm).map(|v| v.to_string().chars().count() == 1) {
if b {
Ok(value.clone())
} else {
Err(vm.new_type_error("one character unicode string expected".to_string()))
}
} else {
Err(vm.new_type_error(format!(
"unicode string expected instead of {} instance",
value.class().name()
)))
}
}
"b" | "h" | "H" | "i" | "I" | "l" | "q" | "L" | "Q" => {
if value.clone().downcast_exact::<PyInt>(vm).is_ok() {
Ok(value.clone())
} else {
Err(vm.new_type_error(format!(
"an integer is required (got type {})",
value.class().name()
)))
}
}
"f" | "d" | "g" => {
if value.clone().downcast_exact::<PyFloat>(vm).is_ok() {
Ok(value.clone())
} else {
Err(vm.new_type_error(format!("must be real number, not {}", value.class().name())))
}
}
"?" => Ok(PyObjectRef::from(
vm.ctx.new_bool(value.clone().try_to_bool(vm)?),
)),
"B" => {
if value.clone().downcast_exact::<PyInt>(vm).is_ok() {
Ok(vm.new_pyobj(u8::try_from_object(vm, value.clone())?))
} else {
Err(vm.new_type_error(format!("int expected instead of {}", value.class().name())))
}
}
"z" => {
if value.clone().downcast_exact::<PyInt>(vm).is_ok()
|| value.clone().downcast_exact::<PyBytes>(vm).is_ok()
{
Ok(value.clone())
} else {
Err(vm.new_type_error(format!(
"bytes or integer address expected instead of {} instance",
value.class().name()
)))
}
}
"Z" => {
if value.clone().downcast_exact::<PyStr>(vm).is_ok() {
Ok(value.clone())
} else {
Err(vm.new_type_error(format!(
"unicode string or integer address expected instead of {} instance",
value.class().name()
)))
}
}
_ => {
// "P"
if value.clone().downcast_exact::<PyInt>(vm).is_ok()
|| value.clone().downcast_exact::<PyNone>(vm).is_ok()
{
Ok(value.clone())
} else {
Err(vm.new_type_error("cannot be converted to pointer".to_string()))
}
}
}
}

pub struct RawBuffer {
#[allow(dead_code)]
pub inner: Box<[u8]>,
#[allow(dead_code)]
pub size: usize,
}

#[pyclass(name = "_CData", module = "_ctypes")]
pub struct PyCData {
_objects: AtomicCell<Vec<PyObjectRef>>,
_buffer: PyRwLock<RawBuffer>,
}

#[pyclass]
impl PyCData {}

#[pyclass(
name = "_SimpleCData",
base = "PyCData",
module = "_ctypes"
// TODO: metaclass
)]
#[derive(PyPayload)]
pub struct PyCSimple {
pub _type_: String,
pub _value: AtomicCell<PyObjectRef>,
}

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

#[pyclass(flags(BASETYPE))]
impl PyCSimple {}

pub fn extend_module_nodes(vm: &VirtualMachine, module: &Py<PyModule>) {
let ctx = &vm.ctx;
extend_module!(vm, module, {
"_CData" => PyCData::make_class(ctx),
"_SimpleCData" => PyCSimple::make_class(ctx),
})
}
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.