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
1 change: 0 additions & 1 deletion Lib/test/test_gc.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,6 @@ def test_function(self):
# is 3 because it includes f's code object.
self.assertIn(gc.collect(), (2, 3))

@unittest.expectedFailure # TODO: RUSTPYTHON; - weakref clear ordering differs from 3.15+
def test_function_tp_clear_leaves_consistent_state(self):
# https://github.com/python/cpython/issues/91636
code = """if 1:
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_weakref.py
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,6 @@ class D:
del c1, c2, C, D
gc.collect()

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_callback_in_cycle_resurrection(self):
import gc

Expand Down Expand Up @@ -798,7 +797,6 @@ def C_went_away(ignore):
gc.collect()
self.assertEqual(alist, [])

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_callbacks_on_callback(self):
import gc

Expand Down
28 changes: 17 additions & 11 deletions benches/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,23 @@ 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() });
// SAFETY: compilation and execution retain no objects beyond this entry.
unsafe {
builder
.add_native_modules(&defs)
.build()
.enter_unchecked(|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
99 changes: 51 additions & 48 deletions benches/microbenchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,58 +111,61 @@ 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();
interp.enter(|vm| {
let setup_code = vm
.compile(&bench.setup, Mode::Exec, &bench.name)
.expect("Error compiling setup code");
let bench_code = vm
.compile(&bench.code, Mode::Exec, &bench.name)
.expect("Error compiling bench code");

let bench_func = |scope| {
let res: PyResult = vm.run_code_obj(bench_code.clone(), scope);
vm.unwrap_pyresult(res);
};
let defs = rustpython_stdlib::stdlib_module_defs(unsafe { builder.context() });
let interp = unsafe { builder.add_native_modules(&defs) }.build();
// SAFETY: benchmark scopes and code objects are released before leaving.
unsafe {
interp.enter_unchecked(|vm| {
let setup_code = vm
.compile(&bench.setup, Mode::Exec, &bench.name)
.expect("Error compiling setup code");
let bench_code = vm
.compile(&bench.code, Mode::Exec, &bench.name)
.expect("Error compiling bench code");

let bench_func = |scope| {
let res: PyResult = vm.run_code_obj(bench_code.clone(), scope);
vm.unwrap_pyresult(res);
};

let bench_setup = |iterations| {
let scope = vm.new_scope_with_builtins();
if let Some(idx) = iterations {
let bench_setup = |iterations| {
let scope = vm.new_scope_with_builtins();
if let Some(idx) = iterations {
scope
.locals
.as_ref()
.expect("new_scope_with_builtins always provides locals")
.as_object()
.set_item("ITERATIONS", vm.new_pyobj(idx), vm)
.expect("Error adding ITERATIONS local variable");
}
let setup_result = vm.run_code_obj(setup_code.clone(), scope.clone());
vm.unwrap_pyresult(setup_result);
scope
.locals
.as_ref()
.expect("new_scope_with_builtins always provides locals")
.as_object()
.set_item("ITERATIONS", vm.new_pyobj(idx), vm)
.expect("Error adding ITERATIONS local variable");
}
let setup_result = vm.run_code_obj(setup_code.clone(), scope.clone());
vm.unwrap_pyresult(setup_result);
scope
};
};

if bench.iterate {
for idx in iteration_counts() {
group.throughput(Throughput::Elements(idx as u64));
group.bench_with_input(
BenchmarkId::new("rustpython", &bench.name),
&idx,
|b, idx| {
b.iter_batched(
|| bench_setup(Some(*idx)),
bench_func,
BatchSize::LargeInput,
);
},
);
if bench.iterate {
for idx in iteration_counts() {
group.throughput(Throughput::Elements(idx as u64));
group.bench_with_input(
BenchmarkId::new("rustpython", &bench.name),
&idx,
|b, idx| {
b.iter_batched(
|| bench_setup(Some(*idx)),
bench_func,
BatchSize::LargeInput,
);
},
);
}
} else {
group.bench_function(BenchmarkId::new("rustpython", &bench.name), move |b| {
b.iter_batched(|| bench_setup(None), bench_func, BatchSize::LargeInput);
});
}
} else {
group.bench_function(BenchmarkId::new("rustpython", &bench.name), move |b| {
b.iter_batched(|| bench_setup(None), bench_func, BatchSize::LargeInput);
});
}
})
})
}
}

/// `true` when the benchmarks are executed by the CodSpeed runner.
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
7 changes: 5 additions & 2 deletions crates/capi/src/objimpl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ pub unsafe extern "C" fn PyObject_GC_Track(op: *mut PyObject) {
with_vm(|_vm| {
let obj = unsafe { op.assume_borrowed() };
if !obj.is_gc_tracked() {
unsafe { gc_state::gc_state().track_object(obj.into(), gc_state::current_owner()) };
unsafe {
gc_state::gc_state_unchecked()
.track_object(obj.into(), gc_state::current_owner_unchecked())
};
}
})
}
Expand All @@ -20,7 +23,7 @@ pub unsafe extern "C" fn PyObject_GC_UnTrack(op: *mut PyObject) {
with_vm(|_vm| {
let obj = unsafe { op.assume_borrowed() };
if obj.is_gc_tracked() {
unsafe { gc_state::gc_state().untrack_object(obj.into()) };
unsafe { gc_state::gc_state_unchecked().untrack_object(obj.into()) };
}
})
}
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(|config| {
let path = rustpython_pylib::LIB_PATH.to_owned();

config.paths.stdlib_dir = Some(path.clone());
config.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
Loading
Loading