Skip to content

Commit 862797d

Browse files
kangdorayouknowone
andauthored
Add new_payload_exception helper for constructing built-in payload exceptions (#8403)
* Add new_payload_exception helper for built-in payload exceptions * Use new_payload_exception for SystemExit raise sites * Use new_payload_exception for BlockingIoError raise sites * Use new_payload_exception for OSError errno dispatch in slot_new * Run rustfmt * Run clippy * Add type guard to new_payload_exception and extract new_system_exit * Extract new_system_exit and fix stale expect messages The four SystemExit raise sites repeated the same construction, so route them through a new_system_exit helper next to new_stop_iteration. The expect messages still described invoke_exception's downcast failure, which no longer applies after moving to new_payload_exception. * Update crates/vm/src/vm/vm_new.rs --------- Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
1 parent 6b747b5 commit 862797d

7 files changed

Lines changed: 82 additions & 51 deletions

File tree

‎crates/vm/src/exceptions.rs‎

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,14 +1389,8 @@ impl OSErrorBuilder {
13891389
vec![strerror.to_pyobject(vm)]
13901390
};
13911391

1392-
let payload = PyOSError::py_new(&exc_type, args.clone().into(), vm)
1393-
.expect("new_os_error usage error");
1394-
let os_error = payload
1395-
.into_ref_with_type_lazy_dict(vm, exc_type)
1396-
.expect("new_os_error usage error");
1397-
PyOSError::slot_init(os_error.as_object().to_owned(), args.into(), vm)
1398-
.expect("new_os_error usage error");
1399-
os_error
1392+
vm.new_payload_exception::<PyOSError>(exc_type, args.into())
1393+
.expect("new_os_error usage error")
14001394
}
14011395
}
14021396

@@ -2148,7 +2142,10 @@ pub(super) mod types {
21482142
.downcast_ref::<PyInt>()
21492143
.and_then(|errno| errno.try_to_primitive::<i32>(vm).ok())
21502144
.and_then(|errno| super::errno_to_exc_type(errno, vm))
2151-
.and_then(|typ| vm.invoke_exception(typ, args_vec).ok())
2145+
.and_then(|typ| {
2146+
vm.new_payload_exception::<Self>(typ.to_owned(), args_vec.into())
2147+
.ok()
2148+
})
21522149
{
21532150
return error.to_pyresult(vm);
21542151
}

‎crates/vm/src/stdlib/_io.rs‎

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ cfg_select! {
2020
}
2121

2222
use crate::{
23-
AsObject, PyObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine, builtins::PyModule,
23+
AsObject, PyObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine,
24+
builtins::{PyModule, PyOSError},
2425
};
2526
pub use _io::{OpenArgs, io_open as open};
2627
use rustpython_host_env::io as host_io;
@@ -943,14 +944,17 @@ mod _io {
943944
Some(n) => n,
944945
None => {
945946
// BlockingIOError(errno, msg, characters_written=0)
946-
return Err(vm.invoke_exception(
947-
vm.ctx.exceptions.blocking_io_error,
948-
vec![
949-
vm.new_pyobj(EAGAIN),
950-
vm.new_pyobj("write could not complete without blocking"),
951-
vm.new_pyobj(0),
952-
],
953-
)?);
947+
return Err(vm
948+
.new_payload_exception::<PyOSError>(
949+
vm.ctx.exceptions.blocking_io_error.to_owned(),
950+
vec![
951+
vm.new_pyobj(EAGAIN),
952+
vm.new_pyobj("write could not complete without blocking"),
953+
vm.new_pyobj(0),
954+
]
955+
.into(),
956+
)?
957+
.upcast());
954958
}
955959
};
956960
self.write_pos += n as Offset;
@@ -1154,14 +1158,17 @@ mod _io {
11541158
self.buffer[self.write_end as usize..][..avail].copy_from_slice(&buf[..avail]);
11551159
self.write_end += avail as Offset;
11561160
self.pos += avail as Offset;
1157-
return Err(vm.invoke_exception(
1158-
vm.ctx.exceptions.blocking_io_error,
1159-
vec![
1160-
vm.new_pyobj(EAGAIN),
1161-
vm.new_pyobj("write could not complete without blocking"),
1162-
vm.new_pyobj(avail),
1163-
],
1164-
)?);
1161+
return Err(vm
1162+
.new_payload_exception::<PyOSError>(
1163+
vm.ctx.exceptions.blocking_io_error.to_owned(),
1164+
vec![
1165+
vm.new_pyobj(EAGAIN),
1166+
vm.new_pyobj("write could not complete without blocking"),
1167+
vm.new_pyobj(avail),
1168+
]
1169+
.into(),
1170+
)?
1171+
.upcast());
11651172
}
11661173
Err(e) => return Err(e),
11671174
}
@@ -1200,14 +1207,17 @@ mod _io {
12001207
self.write_end = buffer_size;
12011208
// BlockingIOError(errno, msg, characters_written)
12021209
let chars_written = written + buffer_len;
1203-
return Err(vm.invoke_exception(
1204-
vm.ctx.exceptions.blocking_io_error,
1205-
vec![
1206-
vm.new_pyobj(EAGAIN),
1207-
vm.new_pyobj("write could not complete without blocking"),
1208-
vm.new_pyobj(chars_written),
1209-
],
1210-
)?);
1210+
return Err(vm
1211+
.new_payload_exception::<PyOSError>(
1212+
vm.ctx.exceptions.blocking_io_error.to_owned(),
1213+
vec![
1214+
vm.new_pyobj(EAGAIN),
1215+
vm.new_pyobj("write could not complete without blocking"),
1216+
vm.new_pyobj(chars_written),
1217+
]
1218+
.into(),
1219+
)?
1220+
.upcast());
12111221
}
12121222
None => break,
12131223
}

‎crates/vm/src/stdlib/_thread.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ pub(crate) mod _thread {
635635

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

641641
thread_local!(static SENTINELS: RefCell<Vec<PyRef<Lock>>> = const { RefCell::new(Vec::new()) });

‎crates/vm/src/stdlib/builtins.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,7 @@ mod builtins {
10411041
#[pyfunction]
10421042
pub(super) fn exit(exit_code_arg: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult {
10431043
let code = exit_code_arg.unwrap_or_else(|| vm.ctx.new_int(0).into());
1044-
Err(vm.invoke_exception(vm.ctx.exceptions.system_exit, vec![code])?)
1044+
Err(vm.new_system_exit(vec![code].into()))
10451045
}
10461046

10471047
#[derive(Debug, Default, FromArgs)]

‎crates/vm/src/stdlib/sys.rs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -776,8 +776,7 @@ pub mod sys {
776776
} else {
777777
vec![status]
778778
};
779-
let exc = vm.invoke_exception(vm.ctx.exceptions.system_exit, args)?;
780-
Err(exc)
779+
Err(vm.new_system_exit(args.into()))
781780
}
782781

783782
#[pyfunction]

‎crates/vm/src/vm/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2737,7 +2737,7 @@ impl VirtualMachine {
27372737
if self.state.finalizing.load(Ordering::Acquire) && !self.is_main_thread() {
27382738
// once finalization starts,
27392739
// non-main Python threads should stop running bytecode.
2740-
return Err(self.invoke_exception(self.ctx.exceptions.system_exit, vec![])?);
2740+
return Err(self.new_system_exit(vec![].into()));
27412741
}
27422742

27432743
// Suspend this thread if stop-the-world is in progress

‎crates/vm/src/vm/vm_new.rs‎

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,18 @@ use rustpython_compiler::{CompileError, ParseError};
1212
use crate::{
1313
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
1414
builtins::{
15-
PyBaseException, PyBaseExceptionRef, PyBytesRef, PyDictRef, PyModule, PyOSError, PyStrRef,
16-
PyType, PyTypeRef,
15+
PyBaseException, PyBaseExceptionRef, PyBytesRef, PyDictRef, PyModule, PyOSError,
16+
PyStopIteration, PyStrRef, PySystemExit, PyType, PyTypeRef,
1717
builtin_func::PyNativeFunction,
1818
descriptor::PyMethodDescriptor,
1919
tuple::{IntoPyTuple, PyTupleRef},
2020
},
2121
convert::{ToPyException, ToPyObject},
2222
exceptions::OSErrorBuilder,
23-
function::{IntoPyNativeFn, PyMethodFlags},
23+
function::{FuncArgs, IntoPyNativeFn, PyMethodFlags},
2424
scope::Scope,
2525
set_attrs,
26+
types::{Constructor, Initializer},
2627
vm::VirtualMachine,
2728
};
2829

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

357+
/// Construct a built-in exception type that carries a payload, directly
358+
/// (`py_new` + `slot_init`), without routing through `PyType::call`.
359+
/// Only valid for a built-in `T` whose exact type is known at compile time.
360+
pub fn new_payload_exception<T>(&self, cls: PyTypeRef, args: FuncArgs) -> PyResult<PyRef<T>>
361+
where
362+
T: Constructor<Args = FuncArgs> + Initializer,
363+
{
364+
debug_assert_eq!(
365+
cls.slots.basicsize,
366+
size_of::<T>(),
367+
"vm.new_payload_exception::<{}>() called with mismatched type '{}'",
368+
core::any::type_name::<T>(),
369+
cls.name()
370+
);
371+
let payload = T::py_new(&cls, args.clone(), self)?;
372+
let exc = payload.into_ref_with_type_lazy_dict(self, cls)?;
373+
T::slot_init(exc.as_object().to_owned(), args, self)?;
374+
Ok(exc)
375+
}
376+
356377
pub fn new_os_error(&self, msg: impl ToPyObject) -> PyRef<PyBaseException> {
357378
self.new_os_subtype_error(self.ctx.exceptions.os_error.to_owned(), None, msg)
358379
.upcast()
@@ -905,16 +926,20 @@ impl VirtualMachine {
905926
exc
906927
}
907928

908-
pub fn new_stop_iteration(&self, value: Option<PyObjectRef>) -> PyBaseExceptionRef {
909-
let stop_iteration_error = self.ctx.exceptions.stop_iteration;
910-
let args = if let Some(value) = value {
911-
vec![value]
912-
} else {
913-
Vec::new()
914-
};
915-
let exc = self.invoke_exception(stop_iteration_error, args);
929+
pub fn new_system_exit(&self, args: FuncArgs) -> PyBaseExceptionRef {
930+
self.new_payload_exception::<PySystemExit>(self.ctx.exceptions.system_exit.to_owned(), args)
931+
.expect("SystemExit construction from internal args is infallible")
932+
.upcast()
933+
}
916934

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

920945
fn new_downcast_error(

0 commit comments

Comments
 (0)