Skip to content
15 changes: 6 additions & 9 deletions crates/vm/src/exceptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1385,14 +1385,8 @@ impl OSErrorBuilder {
vec![strerror.to_pyobject(vm)]
};

let payload = PyOSError::py_new(&exc_type, args.clone().into(), vm)
.expect("new_os_error usage error");
let os_error = payload
.into_ref_with_type_lazy_dict(vm, exc_type)
.expect("new_os_error usage error");
PyOSError::slot_init(os_error.as_object().to_owned(), args.into(), vm)
.expect("new_os_error usage error");
os_error
vm.new_payload_exception::<PyOSError>(exc_type, args.into())
.expect("new_os_error usage error")
}
}

Expand Down Expand Up @@ -2144,7 +2138,10 @@ pub(super) mod types {
.downcast_ref::<PyInt>()
.and_then(|errno| errno.try_to_primitive::<i32>(vm).ok())
.and_then(|errno| super::errno_to_exc_type(errno, vm))
.and_then(|typ| vm.invoke_exception(typ, args_vec).ok())
.and_then(|typ| {
vm.new_payload_exception::<Self>(typ.to_owned(), args_vec.into())
.ok()
})
{
return error.to_pyresult(vm);
}
Expand Down
60 changes: 35 additions & 25 deletions crates/vm/src/stdlib/_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ cfg_select! {
}

use crate::{
AsObject, PyObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine, builtins::PyModule,
AsObject, PyObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine,
builtins::{PyModule, PyOSError},
};
pub use _io::{OpenArgs, io_open as open};
use rustpython_host_env::io as host_io;
Expand Down Expand Up @@ -943,14 +944,17 @@ mod _io {
Some(n) => n,
None => {
// BlockingIOError(errno, msg, characters_written=0)
return Err(vm.invoke_exception(
vm.ctx.exceptions.blocking_io_error,
vec![
vm.new_pyobj(EAGAIN),
vm.new_pyobj("write could not complete without blocking"),
vm.new_pyobj(0),
],
)?);
return Err(vm
.new_payload_exception::<PyOSError>(
vm.ctx.exceptions.blocking_io_error.to_owned(),
vec![
vm.new_pyobj(EAGAIN),
vm.new_pyobj("write could not complete without blocking"),
vm.new_pyobj(0),
]
.into(),
)?
.upcast());
}
};
self.write_pos += n as Offset;
Expand Down Expand Up @@ -1154,14 +1158,17 @@ mod _io {
self.buffer[self.write_end as usize..][..avail].copy_from_slice(&buf[..avail]);
self.write_end += avail as Offset;
self.pos += avail as Offset;
return Err(vm.invoke_exception(
vm.ctx.exceptions.blocking_io_error,
vec![
vm.new_pyobj(EAGAIN),
vm.new_pyobj("write could not complete without blocking"),
vm.new_pyobj(avail),
],
)?);
return Err(vm
.new_payload_exception::<PyOSError>(
vm.ctx.exceptions.blocking_io_error.to_owned(),
vec![
vm.new_pyobj(EAGAIN),
vm.new_pyobj("write could not complete without blocking"),
vm.new_pyobj(avail),
]
.into(),
)?
.upcast());
}
Err(e) => return Err(e),
}
Expand Down Expand Up @@ -1200,14 +1207,17 @@ mod _io {
self.write_end = buffer_size;
// BlockingIOError(errno, msg, characters_written)
let chars_written = written + buffer_len;
return Err(vm.invoke_exception(
vm.ctx.exceptions.blocking_io_error,
vec![
vm.new_pyobj(EAGAIN),
vm.new_pyobj("write could not complete without blocking"),
vm.new_pyobj(chars_written),
],
)?);
return Err(vm
.new_payload_exception::<PyOSError>(
vm.ctx.exceptions.blocking_io_error.to_owned(),
vec![
vm.new_pyobj(EAGAIN),
vm.new_pyobj("write could not complete without blocking"),
vm.new_pyobj(chars_written),
]
.into(),
)?
.upcast());
}
None => break,
}
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/stdlib/_thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ pub(crate) mod _thread {

#[pyfunction]
fn exit(vm: &VirtualMachine) -> PyResult {
Err(vm.invoke_exception(vm.ctx.exceptions.system_exit, vec![])?)
Err(vm.new_system_exit(vec![].into()))
}

thread_local!(static SENTINELS: RefCell<Vec<PyRef<Lock>>> = const { RefCell::new(Vec::new()) });
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/stdlib/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,7 @@ mod builtins {
#[pyfunction]
pub(super) fn exit(exit_code_arg: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult {
let code = exit_code_arg.unwrap_or_else(|| vm.ctx.new_int(0).into());
Err(vm.invoke_exception(vm.ctx.exceptions.system_exit, vec![code])?)
Err(vm.new_system_exit(vec![code].into()))
}

#[derive(Debug, Default, FromArgs)]
Expand Down
3 changes: 1 addition & 2 deletions crates/vm/src/stdlib/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -776,8 +776,7 @@ pub mod sys {
} else {
vec![status]
};
let exc = vm.invoke_exception(vm.ctx.exceptions.system_exit, args)?;
Err(exc)
Err(vm.new_system_exit(args.into()))
}

#[pyfunction]
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2167,7 +2167,7 @@ impl VirtualMachine {
if self.state.finalizing.load(Ordering::Acquire) && !self.is_main_thread() {
// once finalization starts,
// non-main Python threads should stop running bytecode.
return Err(self.invoke_exception(self.ctx.exceptions.system_exit, vec![])?);
return Err(self.new_system_exit(vec![].into()));
}

// Suspend this thread if stop-the-world is in progress
Expand Down
49 changes: 37 additions & 12 deletions crates/vm/src/vm/vm_new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ use rustpython_compiler::{CompileError, ParseError};
use crate::{
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
builtins::{
PyBaseException, PyBaseExceptionRef, PyBytesRef, PyDictRef, PyModule, PyOSError, PyStrRef,
PyType, PyTypeRef,
PyBaseException, PyBaseExceptionRef, PyBytesRef, PyDictRef, PyModule, PyOSError,
PyStopIteration, PyStrRef, PySystemExit, PyType, PyTypeRef,
builtin_func::PyNativeFunction,
descriptor::PyMethodDescriptor,
tuple::{IntoPyTuple, PyTupleRef},
},
convert::{ToPyException, ToPyObject},
exceptions::OSErrorBuilder,
function::{IntoPyNativeFn, PyMethodFlags},
function::{FuncArgs, IntoPyNativeFn, PyMethodFlags},
scope::Scope,
set_attrs,
types::{Constructor, Initializer},
vm::VirtualMachine,
};

Expand Down Expand Up @@ -353,6 +354,26 @@ impl VirtualMachine {
.expect("vm.new_exception() called with an invalid exception type")
}

/// Construct a built-in exception type that carries a payload, directly
/// (`py_new` + `slot_init`), without routing through `PyType::call`.
/// Only valid for a built-in `T` whose exact type is known at compile time.
pub fn new_payload_exception<T>(&self, cls: PyTypeRef, args: FuncArgs) -> PyResult<PyRef<T>>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to rename this to make_exception, and rename the old new_exception to new_simple_exception to give moderate sense to each behavior.

@ShaharNaveh could you advise if there are better names in your mind?

where
T: Constructor<Args = FuncArgs> + Initializer,
{
debug_assert_eq!(
cls.slots.basicsize,
size_of::<T>(),
"vm.new_payload_exception::<{}>() called with mismatched type '{}'",
core::any::type_name::<T>(),
cls.name()
);
let payload = T::py_new(&cls, args.clone(), self)?;
let exc = payload.into_ref_with_type_lazy_dict(self, cls)?;
Comment thread
kangdora marked this conversation as resolved.
T::slot_init(exc.as_object().to_owned(), args, self)?;
Ok(exc)
}

pub fn new_os_error(&self, msg: impl ToPyObject) -> PyRef<PyBaseException> {
self.new_os_subtype_error(self.ctx.exceptions.os_error.to_owned(), None, msg)
.upcast()
Expand Down Expand Up @@ -893,16 +914,20 @@ impl VirtualMachine {
exc
}

pub fn new_stop_iteration(&self, value: Option<PyObjectRef>) -> PyBaseExceptionRef {
let stop_iteration_error = self.ctx.exceptions.stop_iteration;
let args = if let Some(value) = value {
vec![value]
} else {
Vec::new()
};
let exc = self.invoke_exception(stop_iteration_error, args);
pub fn new_system_exit(&self, args: FuncArgs) -> PyBaseExceptionRef {
self.new_payload_exception::<PySystemExit>(self.ctx.exceptions.system_exit.to_owned(), args)
.expect("SystemExit construction from internal args is infallible")
.upcast()
}

exc.expect("StopIteration is a BaseException Subclass.")
pub fn new_stop_iteration(&self, value: Option<PyObjectRef>) -> PyBaseExceptionRef {
let args: FuncArgs = value.map(|v| vec![v]).unwrap_or_default().into();
self.new_payload_exception::<PyStopIteration>(
self.ctx.exceptions.stop_iteration.to_owned(),
args,
)
.expect("StopIteration construction from internal args is infallible")
.upcast()
}

fn new_downcast_error(
Expand Down
Loading