diff --git a/crates/capi/src/import.rs b/crates/capi/src/import.rs index 3a8dae651c9..71496ff7637 100644 --- a/crates/capi/src/import.rs +++ b/crates/capi/src/import.rs @@ -1,14 +1,15 @@ use crate::util::CStrExt; use crate::{PyObject, pystate::with_vm}; use core::ffi::c_char; -use rustpython_vm::builtins::{PyCode, PyDict, PyModule, PyStr}; +use rustpython_vm::builtins::{PyCode, PyDict, PyModule, PyStr, PyTuple}; use rustpython_vm::import::import_code_obj; #[unsafe(no_mangle)] pub unsafe extern "C" fn PyImport_Import(name: *mut PyObject) -> *mut PyObject { with_vm(|vm| { let name = unsafe { (&*name).try_downcast_ref::(vm)? }; - vm.import(name, 0) + let from_list = PyTuple::new_ref_typed(vec![vm.ctx.new_str("*")], &vm.ctx); + vm.import_from(name, &from_list, 0) }) } @@ -74,4 +75,12 @@ mod tests { let _module = py.import("types").unwrap(); }) } + + #[test] + fn import_sub_module() { + Python::attach(|py| { + let module = py.import("collections.abc").unwrap(); + module.getattr("Sequence").unwrap(); + }) + } } diff --git a/crates/capi/src/methodobject.rs b/crates/capi/src/methodobject.rs index cc3676ef51a..2be54ca8939 100644 --- a/crates/capi/src/methodobject.rs +++ b/crates/capi/src/methodobject.rs @@ -53,6 +53,7 @@ pub(crate) fn build_method_def( let flags = PyMethodFlags::from_bits(ml.ml_flags as u32) .ok_or_else(|| vm.new_system_error("PyMethodDef contains unknown flags"))?; + let has_self = has_self && !flags.contains(PyMethodFlags::STATIC); let method = ml.ml_meth; @@ -359,4 +360,17 @@ mod tests { ); }) } + + #[test] + fn wrap_static_no_args_function() { + #[pyfunction()] + fn f() {} + + Python::attach(|py| { + let module = PyModule::new(py, "test_wrap_pyfunction_forms").unwrap(); + + let func = wrap_pyfunction!(f, &module).unwrap(); + func.call0().unwrap(); + }); + } } diff --git a/crates/capi/src/object.rs b/crates/capi/src/object.rs index eabfbef23a1..641dbe3ef9f 100644 --- a/crates/capi/src/object.rs +++ b/crates/capi/src/object.rs @@ -217,7 +217,7 @@ pub unsafe extern "C" fn PyObject_HasAttrWithError( with_vm(|vm| { let obj = unsafe { &*obj }; let name = unsafe { &*attr_name }.try_downcast_ref::(vm)?; - obj.has_attr(name, vm) + Ok(vm.get_attribute_opt(obj.to_owned(), name)?.is_some()) }) } @@ -272,7 +272,7 @@ pub unsafe extern "C" fn PyObject_HasAttrStringWithError( with_vm(|vm| { let obj = unsafe { &*obj }; let name = unsafe { attr_name.try_as_str(vm) }?; - obj.has_attr(name, vm) + Ok(vm.get_attribute_opt(obj.to_owned(), name)?.is_some()) }) } @@ -591,4 +591,16 @@ mod tests { assert!(dict.get_item("foo").is_ok()); }) } + + #[test] + fn hasattr() { + Python::attach(|py| { + let x = 5i32.into_pyobject(py).unwrap(); + assert!(x.is_instance_of::()); + + // spell-checker:ignore bbbbbbytes + assert!(x.hasattr("to_bytes").unwrap()); + assert!(!x.hasattr("bbbbbbytes").unwrap()); + }) + } } diff --git a/crates/capi/src/pycapsule.rs b/crates/capi/src/pycapsule.rs index b36dea3d946..f5b792eec4d 100644 --- a/crates/capi/src/pycapsule.rs +++ b/crates/capi/src/pycapsule.rs @@ -48,6 +48,11 @@ pub unsafe extern "C" fn PyCapsule_GetContext(capsule: *mut PyObject) -> *mut c_ let capsule = unsafe { &*capsule } .downcast_ref_if_exact::(vm) .ok_or_else(|| vm.new_value_error("Invalid capsule"))?; + + if capsule.pointer().is_null() { + return Err(vm.new_value_error("Capsule has null pointer")); + } + Ok(capsule.context()) }) } @@ -143,6 +148,7 @@ fn checked_capsule<'a>( #[cfg(test)] mod tests { + use pyo3::ffi; use pyo3::prelude::*; use pyo3::types::PyCapsule; @@ -156,4 +162,21 @@ mod tests { assert_eq!(unsafe { ptr.cast::().as_ref() }, "Some data"); }) } + + #[test] + fn capsule_context_on_invalid_capsule() { + Python::attach(|py| { + let cap = PyCapsule::new_with_value(py, 123u32, c"name").unwrap(); + + // Invalidate the capsule + // SAFETY: intentionally breaking the capsule for testing + unsafe { + ffi::PyCapsule_SetPointer(cap.as_ptr(), core::ptr::null_mut()); + } + + // context() on invalid capsule should fail + let result = cap.context(); + assert!(result.is_err()); + }); + } } diff --git a/crates/vm/src/builtins/capsule.rs b/crates/vm/src/builtins/capsule.rs index 43efa0fb214..553fca89978 100644 --- a/crates/vm/src/builtins/capsule.rs +++ b/crates/vm/src/builtins/capsule.rs @@ -76,7 +76,9 @@ impl Representable for PyCapsule { impl Destructor for PyCapsule { fn del(zelf: &Py, _vm: &VirtualMachine) -> PyResult<()> { - if let Some(destructor) = zelf.destructor() { + if !zelf.pointer().is_null() + && let Some(destructor) = zelf.destructor() + { unsafe { destructor(zelf.as_object().as_raw().cast_mut()) }; } Ok(())