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
1 change: 0 additions & 1 deletion Lib/test/test_descr.py
Original file line number Diff line number Diff line change
Expand Up @@ -1865,7 +1865,6 @@ class D(C):
self.assertEqual(b.foo, 3)
self.assertEqual(b.__class__, D)

@unittest.expectedSuccess # TODO: RUSTPYTHON; The `expectedFailure` here is from CPython, so this test must fail
@unittest.expectedFailure
def test_bad_new(self):
self.assertRaises(TypeError, object.__new__)
Expand Down
22 changes: 12 additions & 10 deletions benches/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,19 @@ fn bench_rustpython_code(b: &mut Bencher<'_>, name: &str, source: &str) {
settings.write_bytecode = false;
settings.user_site_directory = false;
let builder = Interpreter::builder(settings);
let defs = rustpython_stdlib::stdlib_module_defs(&builder.ctx);
builder.add_native_modules(&defs).build().enter(|vm| {
// Note: bench_cpython is both compiling and executing the code.
// As such we compile the code in the benchmark loop as well.
b.iter(|| {
let code = vm.compile(source, Mode::Exec, name).unwrap();
let scope = vm.new_scope_with_builtins();
let res: PyResult = vm.run_code_obj(code, scope);
vm.unwrap_pyresult(res);
let defs = rustpython_stdlib::stdlib_module_defs(unsafe { builder.context() });
unsafe { builder.add_native_modules(&defs) }
.build()
.enter(|vm| {
// Note: bench_cpython is both compiling and executing the code.
// As such we compile the code in the benchmark loop as well.
b.iter(|| {
let code = vm.compile(source, Mode::Exec, name).unwrap();
let scope = vm.new_scope_with_builtins();
let res: PyResult = vm.run_code_obj(code, scope);
vm.unwrap_pyresult(res);
})
})
})
}

pub fn benchmark_file_execution(
Expand Down
4 changes: 2 additions & 2 deletions benches/microbenchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ fn bench_rustpython_code(group: &mut BenchmarkGroup<'_, WallTime>, bench: &Micro
settings.user_site_directory = false;

let builder = Interpreter::builder(settings);
let defs = rustpython_stdlib::stdlib_module_defs(&builder.ctx);
let interp = builder.add_native_modules(&defs).build();
let defs = rustpython_stdlib::stdlib_module_defs(unsafe { builder.context() });
let interp = unsafe { builder.add_native_modules(&defs) }.build();
interp.enter(|vm| {
let setup_code = vm
.compile(&bench.setup, Mode::Exec, &bench.name)
Expand Down
4 changes: 3 additions & 1 deletion crates/capi/src/bytearrayobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ pub unsafe extern "C" fn PyByteArray_Size(bytearray: *mut PyObject) -> isize {
pub unsafe extern "C" fn PyByteArray_AsString(bytearray: *mut PyObject) -> *mut c_char {
with_vm(|vm| {
let bytearray = unsafe { bytearray.assume_borrowed_and_cast::<PyByteArray>(vm) }?;
Ok(bytearray.borrow_buf_mut().as_mut_ptr())
// SAFETY: the C caller owns the PyByteArray_AsString lifetime and
// synchronization contract; no resizable Rust Vec escapes this API.
Ok(unsafe { bytearray.as_mut_ptr_unchecked() })
})
}

Expand Down
2 changes: 1 addition & 1 deletion crates/capi/src/ceval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ mod tests {
#[pyfunction]
fn legacy_locals_is_frame_mapping() -> bool {
let locals = super::PyEval_GetLocals();
rustpython_vm::vm::thread::with_current_vm(|vm| {
crate::pystate::with_current_vm(|vm| {
let frame = vm.current_frame().unwrap();
core::ptr::eq(
locals.cast_const(),
Expand Down
10 changes: 3 additions & 7 deletions crates/capi/src/descrobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::pystate::with_vm;
use crate::util::{CStrExt, FfiPtrExt};
use core::ffi::{c_char, c_int, c_void};
use rustpython_vm::builtins::{
DescriptorMemberDef, MemberAccess, MemberKind, PyDescriptorOwned, PyGetSet, PyMappingProxy,
DescriptorMemberDef, MemberAccess, MemberKind, PyDescriptor, PyGetSet, PyMappingProxy,
PyMemberDescriptor, PyMemberFlags, PyType,
};
use rustpython_vm::common::lock::PyRwLock;
Expand Down Expand Up @@ -40,11 +40,7 @@ impl PyGetSetDef {
}
})
}
pub(crate) fn build(
&self,
ty: &'static Py<PyType>,
vm: &VirtualMachine,
) -> PyResult<PyRef<PyGetSet>> {
pub(crate) fn build(&self, ty: &Py<PyType>, vm: &VirtualMachine) -> PyResult<PyRef<PyGetSet>> {
let name = unsafe { self.name.try_as_str(vm) }?;
let closure = self.closure as usize;

Expand Down Expand Up @@ -167,7 +163,7 @@ impl PyMemberDef {
});

let descriptor = PyMemberDescriptor {
common: PyDescriptorOwned {
common: PyDescriptor {
typ: ty.to_owned(),
name: vm.ctx.intern_str(name),
qualname: PyRwLock::new(None),
Expand Down
2 changes: 1 addition & 1 deletion crates/capi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub fn init_main_interpreter(interpreter: Interpreter) {
let mut interp = get_main_interpreter();
assert!(interp.is_none(), "Main interpreter is already set");
// Safety: Interpreter was not initialized before, so we can safely assume the statics are not used
unsafe { init_exception_statics(&Context::genesis().exceptions) };
unsafe { init_exception_statics(&Context::genesis_unchecked().exceptions) };
*interp = Some(interpreter);
MAIN_INTERP_PTR.store(
interp.as_ref().unwrap() as *const _ as *mut _,
Expand Down
17 changes: 5 additions & 12 deletions crates/capi/src/object/pytype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub unsafe extern "C" fn Py_IS_TYPE(op: *mut PyObject, ty: *mut PyTypeObject) ->
#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyType_GetFlags(ptr: *mut PyTypeObject) -> c_ulong {
let ty = unsafe { ptr.assume_borrowed() };
ty.slots.flags.load().bits() as u32 as c_ulong
ty.effective_flags().bits() as u32 as c_ulong
}

#[unsafe(no_mangle)]
Expand Down Expand Up @@ -227,27 +227,20 @@ pub extern "C" fn PyType_FromSlots(slots: *const PySlot) -> *mut PyObject {
|msg| vm.new_system_error(format!("Failed to create type from slots: {msg}")),
)?;

let attrs = &class.attributes;
let class_static = unsafe { &*((&*class) as *const _) };
let attrs = class.attributes();
for (name, method) in methods {
attrs.insert(
vm.ctx.intern_str(name),
method.build_method(class_static, vm).into(),
method.build_method(&class, vm).into(),
);
}
for getset in getsets {
let name = unsafe { getset.name.try_as_str(vm)? };
attrs.insert(
vm.ctx.intern_str(name),
getset.build(class_static, vm)?.into(),
);
attrs.insert(vm.ctx.intern_str(name), getset.build(&class, vm)?.into());
}
for member in members {
let name = unsafe { member.name.try_as_str(vm)? };
attrs.insert(
vm.ctx.intern_str(name),
member.build(class_static, vm)?.into(),
);
attrs.insert(vm.ctx.intern_str(name), member.build(&class, vm)?.into());
}

Ok(class)
Expand Down
6 changes: 3 additions & 3 deletions crates/capi/src/pyframe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ mod tests {
use rustpython_vm::AsObject;

fn current_frame_ptr() -> *mut super::PyFrameObject {
rustpython_vm::vm::thread::with_current_vm(|vm| {
crate::pystate::with_current_vm(|vm| {
vm.current_frame()
.unwrap()
.as_object()
Expand Down Expand Up @@ -83,7 +83,7 @@ mod tests {

#[pyfunction]
fn frame_globals_is_globals() -> bool {
rustpython_vm::vm::thread::with_current_vm(|vm| {
crate::pystate::with_current_vm(|vm| {
let frame = vm.current_frame().unwrap();
let got = unsafe { super::PyFrame_GetGlobals(current_frame_ptr()) };
let expected = frame.iframe().globals().as_object().as_raw();
Expand All @@ -95,7 +95,7 @@ mod tests {

#[pyfunction]
fn frame_builtins_is_builtins() -> bool {
rustpython_vm::vm::thread::with_current_vm(|vm| {
crate::pystate::with_current_vm(|vm| {
let frame = vm.current_frame().unwrap();
let got = unsafe { super::PyFrame_GetBuiltins(current_frame_ptr()) };
let expected = frame.iframe().builtins().as_object().as_raw();
Expand Down
30 changes: 15 additions & 15 deletions crates/capi/src/pylifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use crate::pystate::ensure_thread_has_vm_attached;
use alloc::ffi::CString;
use core::ffi::{c_char, c_int, c_ulong};
use core::sync::atomic::{AtomicPtr, Ordering};
use rustpython_vm::common::rc::PyRc;
use rustpython_vm::stdlib::sys;
use rustpython_vm::version::{MAJOR, MICRO, MINOR, RUSTPYTHON_BUILD_INFO, VERSION_HEX};
use rustpython_vm::vm::thread::ThreadedVirtualMachine;
Expand All @@ -19,7 +18,7 @@ pub(crate) fn request_vm_from_interpreter() -> ThreadedVirtualMachine {
get_main_interpreter()
.as_ref()
.expect("Interpreter not initialized")
.enter(|vm| vm.new_thread())
.new_thread()
}

#[unsafe(no_mangle)]
Expand All @@ -40,20 +39,21 @@ pub extern "C" fn Py_InitializeEx(_initsigs: c_int) {
let mut interp = get_main_interpreter();
if interp.is_none() {
// Safety: Interpreter was not initialized before, so we can safely assume the statics are not used
unsafe { init_exception_statics(&Context::genesis().exceptions) };
unsafe { init_exception_statics(&Context::genesis_unchecked().exceptions) };
let builder = Interpreter::builder(Default::default());
let defs = rustpython_stdlib::stdlib_module_defs(&builder.ctx);
*interp = builder
.add_native_modules(&defs)
.init_hook(|vm| {
let state = PyRc::get_mut(&mut vm.state).unwrap();
let path = rustpython_pylib::LIB_PATH.to_owned();

state.config.paths.stdlib_dir = Some(path.clone());
state.config.paths.module_search_paths.insert(0, path);
})
.build()
.into();
let defs = rustpython_stdlib::stdlib_module_defs(unsafe { builder.context() });
*interp = unsafe {
builder
.add_native_modules(&defs)
.configure_paths(|paths| {
let path = rustpython_pylib::LIB_PATH.to_owned();

paths.stdlib_dir = Some(path.clone());
paths.module_search_paths.insert(0, path);
})
.build()
.into()
};
MAIN_INTERP_PTR.store(
interp.as_ref().unwrap() as *const _ as *mut _,
Ordering::Release,
Expand Down
20 changes: 14 additions & 6 deletions crates/capi/src/pystate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ use core::ffi::c_int;
use core::sync::atomic::Ordering;
use rustpython_vm::vm::thread::{
CurrentVmAttachState, SavedThreadState, attach_current_thread, release_current_thread,
restore_current_thread, save_current_thread, with_current_vm,
restore_current_thread_unchecked, save_current_thread_unchecked,
};
use rustpython_vm::{Interpreter, VirtualMachine};

// Native C API callbacks already require an attached interpreter and follow
// CPython's ownership rules. Keep that boundary inside this crate.
pub(crate) fn with_current_vm<R>(f: impl FnOnce(&VirtualMachine) -> R) -> R {
unsafe { rustpython_vm::vm::thread::with_current_vm_unchecked(f) }
}

pub(crate) fn with_vm<R: FfiResult<O>, O>(f: impl FnOnce(&VirtualMachine) -> R) -> O {
with_current_vm(|vm| f(vm).into_output(vm))
}
Expand All @@ -28,7 +34,8 @@ pub struct PyThreadState {
/// Make sure this thread has a running vm attached. This only creates a new vm if we don't already
/// have one. So this will only create a new vm when we are in a new thread created outside RustPython.
pub(crate) fn ensure_thread_has_vm_attached() -> CurrentVmAttachState {
attach_current_thread(request_vm_from_interpreter)
// SAFETY: the C API pairs Ensure/Release on the calling native thread.
unsafe { attach_current_thread(request_vm_from_interpreter) }
}

#[unsafe(no_mangle)]
Expand All @@ -42,7 +49,7 @@ pub extern "C" fn PyGILState_Ensure() -> PyGILState_STATE {
#[unsafe(no_mangle)]
pub extern "C" fn PyGILState_Release(state: PyGILState_STATE) {
if state == PYGILSTATE_UNLOCKED {
release_current_thread(CurrentVmAttachState::Attached);
unsafe { release_current_thread(CurrentVmAttachState::Attached) };
}
}

Expand All @@ -51,7 +58,7 @@ pub extern "C" fn PyEval_SaveThread() -> *mut PyThreadState {
let interp = PyInterpreterState_Get();
let state = Box::new(PyThreadState {
interp,
vm: save_current_thread(),
vm: unsafe { save_current_thread_unchecked() },
});
Box::into_raw(state)
}
Expand All @@ -62,7 +69,7 @@ pub unsafe extern "C" fn PyEval_RestoreThread(state: *mut PyThreadState) {
// SAFETY: PyEval_SaveThread returns this allocation and CPython's API
// requires callers to restore exactly that thread state once.
let state = unsafe { Box::from_raw(state) };
restore_current_thread(state.vm);
unsafe { restore_current_thread_unchecked(state.vm) };
}

#[unsafe(no_mangle)]
Expand All @@ -87,10 +94,11 @@ pub extern "C" fn PyInterpreterState_GetID(interp: *mut PyInterpreterState) -> i

#[cfg(test)]
mod tests {
use super::with_current_vm;
use crate::get_main_interpreter;
use crate::pystate::{PyGILState_Ensure, PyGILState_Release};
use pyo3::prelude::*;
use rustpython_vm::vm::thread::{current_vm_is_set, with_current_vm};
use rustpython_vm::vm::thread::current_vm_is_set;

#[test]
fn new_thread() {
Expand Down
Loading
Loading