Skip to content
Open
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
272 changes: 269 additions & 3 deletions crates/capi/src/object.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use crate::PyObject;
use crate::pystate::with_vm;
use core::ffi::{CStr, c_char, c_int, c_uint, c_ulong};
use core::ffi::{CStr, c_char, c_int, c_uint, c_ulong, c_void};
use core::ptr::NonNull;
use rustpython_vm::builtins::{PyStr, PyType};
use rustpython_vm::{AsObject, Py};
use rustpython_vm::builtins::{PyStr, PyType, object_generic_set_dict, object_get_dict};
use rustpython_vm::bytecode::ComparisonOperator;
use rustpython_vm::function::PySetterValue;
use rustpython_vm::{AsObject, Py, PyPayload};

pub type PyTypeObject = Py<PyType>;

Expand Down Expand Up @@ -76,6 +78,11 @@ pub unsafe extern "C" fn PyType_GetQualName(ptr: *const PyTypeObject) -> *mut Py
with_vm(|vm| unsafe { &*ptr }.__qualname__(vm))
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyType_GetModuleName(ptr: *const PyTypeObject) -> *mut PyObject {
with_vm(|vm| unsafe { &*ptr }.__module__(vm))
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyType_GetFullyQualifiedName(ptr: *const PyTypeObject) -> *mut PyObject {
with_vm(|vm| {
Expand Down Expand Up @@ -142,6 +149,29 @@ pub unsafe extern "C" fn PyObject_GetAttrString(
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyObject_GetOptionalAttr(
obj: *mut PyObject,
name: *mut PyObject,
result: *mut *mut PyObject,
) -> c_int {
with_vm(|vm| {
unsafe {
*result = core::ptr::null_mut();
}
let obj = unsafe { &*obj };
let name = unsafe { &*name }.try_downcast_ref::<PyStr>(vm)?;
if let Some(attr) = vm.get_attribute_opt(obj.to_owned(), name)? {
unsafe {
*result = attr.into_raw().as_ptr();
}
Ok(true)
} else {
Ok(false)
}
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyObject_SetAttrString(
obj: *mut PyObject,
Expand Down Expand Up @@ -172,6 +202,30 @@ pub unsafe extern "C" fn PyObject_SetAttr(
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyObject_HasAttrWithError(
obj: *mut PyObject,
attr_name: *mut PyObject,
) -> c_int {
with_vm(|vm| {
let obj = unsafe { &*obj };
let name = unsafe { &*attr_name }.try_downcast_ref::<PyStr>(vm)?;
obj.has_attr(name, vm)
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyObject_GenericGetAttr(
obj: *mut PyObject,
name: *mut PyObject,
) -> *mut PyObject {
with_vm(|vm| {
let obj = unsafe { &*obj };
let name = unsafe { &*name }.try_downcast_ref::<PyStr>(vm)?;
obj.generic_getattr(name, vm)
})
}

#[unsafe(no_mangle)]
pub extern "C" fn PyObject_Repr(obj: *mut PyObject) -> *mut PyObject {
with_vm(|vm| {
Expand All @@ -194,10 +248,222 @@ pub extern "C" fn PyObject_Str(obj: *mut PyObject) -> *mut PyObject {
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyObject_RichCompare(
left: *mut PyObject,
right: *mut PyObject,
op: c_int,
) -> *mut PyObject {
with_vm(|vm| {
let op = match op {
0 => ComparisonOperator::Less,
1 => ComparisonOperator::LessOrEqual,
2 => ComparisonOperator::Equal,
3 => ComparisonOperator::NotEqual,
4 => ComparisonOperator::Greater,
5 => ComparisonOperator::GreaterOrEqual,
_ => return Err(vm.new_system_error("invalid comparison operator")),
};
let left = unsafe { &*left };
let right = unsafe { &*right };
left.to_owned()
.rich_compare(right.to_owned(), op.into(), vm)
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyCallable_Check(obj: *mut PyObject) -> c_int {
with_vm(|_vm| unsafe { obj.as_ref().is_some_and(PyObject::is_callable) })
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyObject_ClearWeakRefs(obj: *mut PyObject) {
with_vm(|_vm| unsafe { &*obj }.clear_weak_refs())
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyObject_Dir(obj: *mut PyObject) -> *mut PyObject {
with_vm(|vm| {
unsafe { obj.as_ref() }
.map_or_else(|| vm.dir(None), |obj| obj.to_owned().dir(vm))
.map(|list| list.into_ref(&vm.ctx))
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyObject_IsTrue(obj: *mut PyObject) -> c_int {
with_vm(|vm| {
let obj = unsafe { &*obj };
obj.to_owned().is_true(vm)
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyObject_GenericGetDict(
obj: *mut PyObject,
_context: *mut c_void,
) -> *mut PyObject {
with_vm(|vm| {
let obj = unsafe { &*obj };
object_get_dict(obj.to_owned(), vm)
})
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyObject_GenericSetDict(
obj: *mut PyObject,
value: *mut PyObject,
_context: *mut c_void,
) -> c_int {
with_vm(|vm| {
let obj = unsafe { &*obj };
let value = match NonNull::new(value) {
Some(value) => PySetterValue::Assign(unsafe { value.as_ref() }.to_owned()),
None => PySetterValue::Delete,
};
object_generic_set_dict(obj.to_owned(), value, vm)
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

#[cfg(false)]
mod tests {
use pyo3::class::basic::CompareOp;
use pyo3::prelude::*;
use pyo3::types::{PyBool, PyDict, PyInt, PyString, PyTypeMethods};

#[test]
fn is_truthy() {
Python::attach(|py| {
assert!(!py.None().is_truthy(py).unwrap());
})
}

#[test]
fn is_none() {
Python::attach(|py| {
assert!(py.None().is_none(py));
})
}

#[test]
fn bool() {
Python::attach(|py| {
assert!(PyBool::new(py, true).is_truthy().unwrap());
assert!(!PyBool::new(py, false).is_truthy().unwrap());
})
}

#[test]
fn type_name() {
Python::attach(|py| {
let string = PyString::new(py, "Hello, World!");
assert_eq!(string.get_type().name().unwrap().to_str().unwrap(), "str");
})
}

#[test]
fn repr() {
Python::attach(|py| {
let module = py.import("sys").unwrap();
assert_eq!(module.repr().unwrap(), "<module 'sys' (built-in)>");
})
}

#[test]
fn obj_to_str() {
Python::attach(|py| {
let number = PyInt::new(py, 42);
assert_eq!(number.str().unwrap(), "42");
})
}

#[test]
fn get_attr() {
Python::attach(|py| {
let sys = py.import("sys").unwrap();
let implementation = sys
.getattr("implementation")
.unwrap()
.getattr("name")
.unwrap()
.str()
.unwrap();

assert_eq!(implementation, "rustpython");
})
}

#[test]
fn callable_check() {
Python::attach(|py| {
let int_type = py.get_type::<PyInt>();
assert!(int_type.is_callable());
assert!(!PyInt::new(py, 42).is_callable());
})
}

#[test]
fn object_dir() {
Python::attach(|py| {
assert!(PyInt::new(py, 42).dir().unwrap().len() > 0);
})
}

#[test]
fn get_optional_attr() {
Python::attach(|py| {
let number = PyInt::new(py, 42);
assert!(number.getattr_opt("real").unwrap().is_some());
assert!(
number
.getattr_opt("attribute_that_should_not_exist")
.unwrap()
.is_none()
);
})
}

#[test]
fn rich_compare() {
Python::attach(|py| {
let lower = PyInt::new(py, 1);
let upper = PyInt::new(py, 2);
assert!(
lower
.rich_compare(upper, CompareOp::Lt)
.unwrap()
.is_truthy()
.unwrap()
);
})
}

#[test]
fn type_get_module_name() {
Python::attach(|py| {
assert_eq!(
py.get_type::<PyInt>().module().unwrap().to_str().unwrap(),
"builtins"
);
})
}

#[test]
fn generic_get_dict() {
Python::attach(|py| {
let globals = PyDict::new(py);
py.run(c"class MyClass: ...", None, Some(&globals)).unwrap();
let my_class = globals.get_item("MyClass").unwrap().unwrap();
let instance = my_class.call0().unwrap();
instance.setattr("foo", 42).unwrap();
let dict = unsafe {
Bound::from_owned_ptr_or_err(
py,
pyo3::ffi::PyObject_GenericGetDict(instance.as_ptr(), core::ptr::null_mut()),
)
}
.unwrap();
assert!(dict.get_item("foo").is_ok());
})
}
}
1 change: 1 addition & 0 deletions crates/vm/src/builtins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub(crate) mod namespace;
pub use namespace::PyNamespace;
pub(crate) mod object;
pub use object::PyBaseObject;
pub use object::{object_generic_set_dict, object_get_dict};
pub(crate) mod property;
pub use property::PyProperty;
#[path = "bool.rs"]
Expand Down
4 changes: 2 additions & 2 deletions crates/vm/src/builtins/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ impl PyBaseObject {
}
}

pub(crate) fn object_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyDictRef> {
pub fn object_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyDictRef> {
if let Some(dict) = obj.dict() {
Ok(dict)
} else {
Expand All @@ -578,7 +578,7 @@ pub(crate) fn object_set_dict(
.map_err(|_| vm.new_attribute_error("This object has no __dict__"))
}

pub(crate) fn object_generic_set_dict(
pub fn object_generic_set_dict(
obj: PyObjectRef,
value: PySetterValue,
vm: &VirtualMachine,
Expand Down
6 changes: 6 additions & 0 deletions crates/vm/src/object/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1445,6 +1445,12 @@ impl PyObject {
self.downgrade_with_typ(callback, vm.ctx.types.weakref_type.to_owned(), vm)
}

pub fn clear_weak_refs(&self) {
if let Some(wrl) = self.weak_ref_list() {
wrl.clear(self);
}
}

pub fn get_weak_references(&self) -> Option<Vec<PyRef<PyWeak>>> {
self.weak_ref_list()
.map(|wrl| wrl.get_weak_references(self))
Expand Down
Loading