From 54c4cbc3a5b156a9e0b0be8a8bd097facc640c28 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sun, 7 Jun 2026 16:06:05 +0300 Subject: [PATCH 1/3] Code nitpicks --- crates/vm/src/protocol/buffer.rs | 25 ++- crates/vm/src/protocol/callable.rs | 12 +- crates/vm/src/protocol/iter.rs | 7 +- crates/vm/src/protocol/mapping.rs | 19 ++- crates/vm/src/protocol/number.rs | 256 ++++++++++++++++------------- crates/vm/src/protocol/object.rs | 79 ++++----- crates/vm/src/protocol/sequence.rs | 38 +++-- crates/vm/src/stdlib/_functools.rs | 6 +- 8 files changed, 237 insertions(+), 205 deletions(-) diff --git a/crates/vm/src/protocol/buffer.rs b/crates/vm/src/protocol/buffer.rs index 8e7667241b3..d79c5e9933d 100644 --- a/crates/vm/src/protocol/buffer.rs +++ b/crates/vm/src/protocol/buffer.rs @@ -44,11 +44,10 @@ pub struct PyBuffer { impl PyBuffer { #[must_use] pub fn new(obj: PyObjectRef, desc: BufferDescriptor, methods: &'static BufferMethods) -> Self { - let zelf = Self { - obj, - desc: desc.validate(), - methods, - }; + #[cfg(debug_assertions)] + let desc = desc.validate(); + + let zelf = Self { obj, desc, methods }; zelf.retain(); zelf } @@ -216,30 +215,25 @@ impl BufferDescriptor { if self.ndim() == 0 { // Empty structures (len=0) can have itemsize=0 if self.len > 0 { - assert!(self.itemsize != 0); + debug_assert_ne!(self.itemsize, 0); } - assert!(self.itemsize == self.len); + debug_assert_eq!(self.itemsize, self.len); } else { let mut shape_product = 1; let has_zero_dim = self.dim_desc.iter().any(|(s, _, _)| *s == 0); for (shape, stride, suboffset) in self.dim_desc.iter().copied() { shape_product *= shape; - assert!(suboffset >= 0); + debug_assert!(suboffset >= 0); // For empty arrays (any dimension is 0), strides can be 0 if !has_zero_dim { - assert!(stride != 0); + debug_assert_ne!(stride, 0); } } - assert!(shape_product * self.itemsize == self.len); + debug_assert_eq!(shape_product * self.itemsize, self.len); } self } - #[cfg(not(debug_assertions))] - pub fn validate(self) -> Self { - self - } - #[must_use] pub fn ndim(&self) -> usize { self.dim_desc.len() @@ -396,6 +390,7 @@ impl BufferDescriptor { } } + #[must_use] fn is_last_dim_contiguous(&self) -> bool { let (_, stride, suboffset) = self.dim_desc[self.ndim() - 1]; suboffset == 0 && stride == self.itemsize as isize diff --git a/crates/vm/src/protocol/callable.rs b/crates/vm/src/protocol/callable.rs index 6ff988abbe6..515eb752027 100644 --- a/crates/vm/src/protocol/callable.rs +++ b/crates/vm/src/protocol/callable.rs @@ -7,11 +7,13 @@ use crate::{ impl PyObject { #[inline] + #[must_use] pub fn to_callable(&self) -> Option> { PyCallable::new(self) } #[inline] + #[must_use] pub fn is_callable(&self) -> bool { self.to_callable().is_some() } @@ -134,6 +136,7 @@ impl<'a> PyCallable<'a> { } /// Trace events for sys.settrace and sys.setprofile. +#[derive(Clone, Copy, Eq, PartialEq)] pub(crate) enum TraceEvent { Call, Return, @@ -147,7 +150,8 @@ pub(crate) enum TraceEvent { impl TraceEvent { /// Whether sys.settrace receives this event. - fn is_trace_event(&self) -> bool { + #[must_use] + const fn is_trace_event(&self) -> bool { matches!( self, Self::Call | Self::Return | Self::Exception | Self::Line | Self::Opcode @@ -157,7 +161,8 @@ impl TraceEvent { /// Whether sys.setprofile receives this event. /// In legacy_tracing.c, profile callbacks are only registered for /// PY_RETURN, PY_UNWIND, C_CALL, C_RETURN, C_RAISE. - fn is_profile_event(&self) -> bool { + #[must_use] + const fn is_profile_event(&self) -> bool { matches!( self, Self::Call | Self::Return | Self::CCall | Self::CReturn | Self::CException @@ -165,7 +170,8 @@ impl TraceEvent { } /// Whether this event is dispatched only when f_trace_opcodes is set. - pub(crate) fn is_opcode_event(&self) -> bool { + #[must_use] + pub(crate) const fn is_opcode_event(&self) -> bool { matches!(self, Self::Opcode) } } diff --git a/crates/vm/src/protocol/iter.rs b/crates/vm/src/protocol/iter.rs index aa6ab6769cd..2f51287b181 100644 --- a/crates/vm/src/protocol/iter.rs +++ b/crates/vm/src/protocol/iter.rs @@ -7,8 +7,7 @@ use crate::{ use core::borrow::Borrow; use core::ops::Deref; -/// Iterator Protocol -// https://docs.python.org/3/c-api/iter.html +/// [Iterator Protocol](https://docs.python.org/3/c-api/iter.html). #[derive(Debug, Clone)] #[repr(transparent)] pub struct PyIter(O) @@ -31,9 +30,11 @@ impl PyIter where O: Borrow, { + #[must_use] pub const fn new(obj: O) -> Self { Self(obj) } + pub fn next(&self, vm: &VirtualMachine) -> PyResult { let iternext = self .0 @@ -193,7 +194,7 @@ impl PyIterReturn { match self { Self::Return(obj) => Ok(obj), Self::StopIteration(v) => Err({ - let args = if let Some(v) = v { vec![v] } else { Vec::new() }; + let args = v.map_or_else(Vec::new, |v| vec![v]); vm.new_exception(vm.ctx.exceptions.stop_async_iteration.to_owned(), args) }), } diff --git a/crates/vm/src/protocol/mapping.rs b/crates/vm/src/protocol/mapping.rs index 7d06c799153..1900e072db3 100644 --- a/crates/vm/src/protocol/mapping.rs +++ b/crates/vm/src/protocol/mapping.rs @@ -1,3 +1,5 @@ +use crossbeam_utils::atomic::AtomicCell; + use crate::{ AsObject, PyObject, PyObjectRef, PyResult, VirtualMachine, builtins::{ @@ -7,12 +9,9 @@ use crate::{ convert::ToPyResult, object::{Traverse, TraverseFn}, }; -use crossbeam_utils::atomic::AtomicCell; - -// Mapping protocol -// https://docs.python.org/3/c-api/mapping.html -#[allow(clippy::type_complexity)] +/// [Mapping protocol](https://docs.python.org/3/c-api/mapping.html) +#[expect(clippy::type_complexity)] #[derive(Default)] pub struct PyMappingSlots { pub length: AtomicCell, &VirtualMachine) -> PyResult>>, @@ -29,25 +28,28 @@ impl core::fmt::Debug for PyMappingSlots { } impl PyMappingSlots { + #[must_use] pub fn has_subscript(&self) -> bool { self.subscript.load().is_some() } - /// Copy from static PyMappingMethods + /// Copy from static [`PyMappingMethods`]. pub fn copy_from(&self, methods: &PyMappingMethods) { if let Some(f) = methods.length { self.length.store(Some(f)); } + if let Some(f) = methods.subscript { self.subscript.store(Some(f)); } + if let Some(f) = methods.ass_subscript { self.ass_subscript.store(Some(f)); } } } -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] #[derive(Default)] pub struct PyMappingMethods { pub length: Option, &VirtualMachine) -> PyResult>, @@ -71,7 +73,8 @@ impl PyMappingMethods { } impl PyObject { - pub fn mapping_unchecked(&self) -> PyMapping<'_> { + #[must_use] + pub const fn mapping_unchecked(&self) -> PyMapping<'_> { PyMapping { obj: self } } diff --git a/crates/vm/src/protocol/number.rs b/crates/vm/src/protocol/number.rs index 448ec85a43e..86b126538ca 100644 --- a/crates/vm/src/protocol/number.rs +++ b/crates/vm/src/protocol/number.rs @@ -141,7 +141,8 @@ pub struct PyNumberMethods { } impl PyNumberMethods { - /// this is NOT a global variable + /// NOTE: + /// This is **NOT** a global variable. Use [`Self::not_implemented`] for a global variable. pub const NOT_IMPLEMENTED: Self = Self { add: None, subtract: None, @@ -181,14 +182,14 @@ impl PyNumberMethods { }; #[must_use] - pub fn not_implemented() -> &'static Self { + pub const fn not_implemented() -> &'static Self { static GLOBAL_NOT_IMPLEMENTED: PyNumberMethods = PyNumberMethods::NOT_IMPLEMENTED; &GLOBAL_NOT_IMPLEMENTED } } /// Matches the NB_* constants ordering from opcode.h / BinaryOperator. -#[derive(Copy, Clone)] +#[derive(Clone, Copy, Eq, PartialEq)] pub enum PyNumberBinaryOp { Add, And, @@ -223,39 +224,38 @@ impl PyNumberBinaryOp { self, vm: &VirtualMachine, ) -> Option<&'static crate::builtins::PyStrInterned> { - use PyNumberBinaryOp::*; Some(match self { - Add => identifier!(vm, __radd__), - Subtract => identifier!(vm, __rsub__), - Multiply => identifier!(vm, __rmul__), - Remainder => identifier!(vm, __rmod__), - Divmod => identifier!(vm, __rdivmod__), - Lshift => identifier!(vm, __rlshift__), - Rshift => identifier!(vm, __rrshift__), - And => identifier!(vm, __rand__), - Xor => identifier!(vm, __rxor__), - Or => identifier!(vm, __ror__), - FloorDivide => identifier!(vm, __rfloordiv__), - TrueDivide => identifier!(vm, __rtruediv__), - MatrixMultiply => identifier!(vm, __rmatmul__), + Self::Add => identifier!(vm, __radd__), + Self::Subtract => identifier!(vm, __rsub__), + Self::Multiply => identifier!(vm, __rmul__), + Self::Remainder => identifier!(vm, __rmod__), + Self::Divmod => identifier!(vm, __rdivmod__), + Self::Lshift => identifier!(vm, __rlshift__), + Self::Rshift => identifier!(vm, __rrshift__), + Self::And => identifier!(vm, __rand__), + Self::Xor => identifier!(vm, __rxor__), + Self::Or => identifier!(vm, __ror__), + Self::FloorDivide => identifier!(vm, __rfloordiv__), + Self::TrueDivide => identifier!(vm, __rtruediv__), + Self::MatrixMultiply => identifier!(vm, __rmatmul__), // In-place ops don't have right-side variants - InplaceAdd - | InplaceSubtract - | InplaceMultiply - | InplaceRemainder - | InplaceLshift - | InplaceRshift - | InplaceAnd - | InplaceXor - | InplaceOr - | InplaceFloorDivide - | InplaceTrueDivide - | InplaceMatrixMultiply => return None, + Self::InplaceAdd + | Self::InplaceSubtract + | Self::InplaceMultiply + | Self::InplaceRemainder + | Self::InplaceLshift + | Self::InplaceRshift + | Self::InplaceAnd + | Self::InplaceXor + | Self::InplaceOr + | Self::InplaceFloorDivide + | Self::InplaceTrueDivide + | Self::InplaceMatrixMultiply => return None, }) } } -#[derive(Copy, Clone)] +#[derive(Clone, Copy, Eq, PartialEq)] pub enum PyNumberTernaryOp { Power, InplacePower, @@ -391,193 +391,224 @@ impl From<&PyNumberMethods> for PyNumberSlots { } impl PyNumberSlots { - /// Copy from static PyNumberMethods + /// Copy from static [`PyNumberMethods`]. pub fn copy_from(&self, methods: &PyNumberMethods) { if let Some(f) = methods.add { self.add.store(Some(f)); self.right_add.store(Some(f)); } + if let Some(f) = methods.subtract { self.subtract.store(Some(f)); self.right_subtract.store(Some(f)); } + if let Some(f) = methods.multiply { self.multiply.store(Some(f)); self.right_multiply.store(Some(f)); } + if let Some(f) = methods.remainder { self.remainder.store(Some(f)); self.right_remainder.store(Some(f)); } + if let Some(f) = methods.divmod { self.divmod.store(Some(f)); self.right_divmod.store(Some(f)); } + if let Some(f) = methods.power { self.power.store(Some(f)); self.right_power.store(Some(f)); } + if let Some(f) = methods.negative { self.negative.store(Some(f)); } + if let Some(f) = methods.positive { self.positive.store(Some(f)); } + if let Some(f) = methods.absolute { self.absolute.store(Some(f)); } + if let Some(f) = methods.boolean { self.boolean.store(Some(f)); } + if let Some(f) = methods.invert { self.invert.store(Some(f)); } + if let Some(f) = methods.lshift { self.lshift.store(Some(f)); self.right_lshift.store(Some(f)); } + if let Some(f) = methods.rshift { self.rshift.store(Some(f)); self.right_rshift.store(Some(f)); } + if let Some(f) = methods.and { self.and.store(Some(f)); self.right_and.store(Some(f)); } + if let Some(f) = methods.xor { self.xor.store(Some(f)); self.right_xor.store(Some(f)); } + if let Some(f) = methods.or { self.or.store(Some(f)); self.right_or.store(Some(f)); } + if let Some(f) = methods.int { self.int.store(Some(f)); } + if let Some(f) = methods.float { self.float.store(Some(f)); } + if let Some(f) = methods.inplace_add { self.inplace_add.store(Some(f)); } + if let Some(f) = methods.inplace_subtract { self.inplace_subtract.store(Some(f)); } + if let Some(f) = methods.inplace_multiply { self.inplace_multiply.store(Some(f)); } + if let Some(f) = methods.inplace_remainder { self.inplace_remainder.store(Some(f)); } + if let Some(f) = methods.inplace_power { self.inplace_power.store(Some(f)); } + if let Some(f) = methods.inplace_lshift { self.inplace_lshift.store(Some(f)); } + if let Some(f) = methods.inplace_rshift { self.inplace_rshift.store(Some(f)); } + if let Some(f) = methods.inplace_and { self.inplace_and.store(Some(f)); } + if let Some(f) = methods.inplace_xor { self.inplace_xor.store(Some(f)); } + if let Some(f) = methods.inplace_or { self.inplace_or.store(Some(f)); } + if let Some(f) = methods.floor_divide { self.floor_divide.store(Some(f)); self.right_floor_divide.store(Some(f)); } + if let Some(f) = methods.true_divide { self.true_divide.store(Some(f)); self.right_true_divide.store(Some(f)); } + if let Some(f) = methods.inplace_floor_divide { self.inplace_floor_divide.store(Some(f)); } + if let Some(f) = methods.inplace_true_divide { self.inplace_true_divide.store(Some(f)); } + if let Some(f) = methods.index { self.index.store(Some(f)); } + if let Some(f) = methods.matrix_multiply { self.matrix_multiply.store(Some(f)); self.right_matrix_multiply.store(Some(f)); } + if let Some(f) = methods.inplace_matrix_multiply { self.inplace_matrix_multiply.store(Some(f)); } } pub fn left_binary_op(&self, op_slot: PyNumberBinaryOp) -> Option { - use PyNumberBinaryOp::*; match op_slot { - Add => self.add.load(), - Subtract => self.subtract.load(), - Multiply => self.multiply.load(), - Remainder => self.remainder.load(), - Divmod => self.divmod.load(), - Lshift => self.lshift.load(), - Rshift => self.rshift.load(), - And => self.and.load(), - Xor => self.xor.load(), - Or => self.or.load(), - InplaceAdd => self.inplace_add.load(), - InplaceSubtract => self.inplace_subtract.load(), - InplaceMultiply => self.inplace_multiply.load(), - InplaceRemainder => self.inplace_remainder.load(), - InplaceLshift => self.inplace_lshift.load(), - InplaceRshift => self.inplace_rshift.load(), - InplaceAnd => self.inplace_and.load(), - InplaceXor => self.inplace_xor.load(), - InplaceOr => self.inplace_or.load(), - FloorDivide => self.floor_divide.load(), - TrueDivide => self.true_divide.load(), - InplaceFloorDivide => self.inplace_floor_divide.load(), - InplaceTrueDivide => self.inplace_true_divide.load(), - MatrixMultiply => self.matrix_multiply.load(), - InplaceMatrixMultiply => self.inplace_matrix_multiply.load(), + PyNumberBinaryOp::Add => self.add.load(), + PyNumberBinaryOp::Subtract => self.subtract.load(), + PyNumberBinaryOp::Multiply => self.multiply.load(), + PyNumberBinaryOp::Remainder => self.remainder.load(), + PyNumberBinaryOp::Divmod => self.divmod.load(), + PyNumberBinaryOp::Lshift => self.lshift.load(), + PyNumberBinaryOp::Rshift => self.rshift.load(), + PyNumberBinaryOp::And => self.and.load(), + PyNumberBinaryOp::Xor => self.xor.load(), + PyNumberBinaryOp::Or => self.or.load(), + PyNumberBinaryOp::InplaceAdd => self.inplace_add.load(), + PyNumberBinaryOp::InplaceSubtract => self.inplace_subtract.load(), + PyNumberBinaryOp::InplaceMultiply => self.inplace_multiply.load(), + PyNumberBinaryOp::InplaceRemainder => self.inplace_remainder.load(), + PyNumberBinaryOp::InplaceLshift => self.inplace_lshift.load(), + PyNumberBinaryOp::InplaceRshift => self.inplace_rshift.load(), + PyNumberBinaryOp::InplaceAnd => self.inplace_and.load(), + PyNumberBinaryOp::InplaceXor => self.inplace_xor.load(), + PyNumberBinaryOp::InplaceOr => self.inplace_or.load(), + PyNumberBinaryOp::FloorDivide => self.floor_divide.load(), + PyNumberBinaryOp::TrueDivide => self.true_divide.load(), + PyNumberBinaryOp::InplaceFloorDivide => self.inplace_floor_divide.load(), + PyNumberBinaryOp::InplaceTrueDivide => self.inplace_true_divide.load(), + PyNumberBinaryOp::MatrixMultiply => self.matrix_multiply.load(), + PyNumberBinaryOp::InplaceMatrixMultiply => self.inplace_matrix_multiply.load(), } } pub fn right_binary_op(&self, op_slot: PyNumberBinaryOp) -> Option { - use PyNumberBinaryOp::*; match op_slot { - Add => self.right_add.load(), - Subtract => self.right_subtract.load(), - Multiply => self.right_multiply.load(), - Remainder => self.right_remainder.load(), - Divmod => self.right_divmod.load(), - Lshift => self.right_lshift.load(), - Rshift => self.right_rshift.load(), - And => self.right_and.load(), - Xor => self.right_xor.load(), - Or => self.right_or.load(), - FloorDivide => self.right_floor_divide.load(), - TrueDivide => self.right_true_divide.load(), - MatrixMultiply => self.right_matrix_multiply.load(), + PyNumberBinaryOp::Add => self.right_add.load(), + PyNumberBinaryOp::Subtract => self.right_subtract.load(), + PyNumberBinaryOp::Multiply => self.right_multiply.load(), + PyNumberBinaryOp::Remainder => self.right_remainder.load(), + PyNumberBinaryOp::Divmod => self.right_divmod.load(), + PyNumberBinaryOp::Lshift => self.right_lshift.load(), + PyNumberBinaryOp::Rshift => self.right_rshift.load(), + PyNumberBinaryOp::And => self.right_and.load(), + PyNumberBinaryOp::Xor => self.right_xor.load(), + PyNumberBinaryOp::Or => self.right_or.load(), + PyNumberBinaryOp::FloorDivide => self.right_floor_divide.load(), + PyNumberBinaryOp::TrueDivide => self.right_true_divide.load(), + PyNumberBinaryOp::MatrixMultiply => self.right_matrix_multiply.load(), _ => None, } } pub fn left_ternary_op(&self, op_slot: PyNumberTernaryOp) -> Option { - use PyNumberTernaryOp::*; match op_slot { - Power => self.power.load(), - InplacePower => self.inplace_power.load(), + PyNumberTernaryOp::Power => self.power.load(), + PyNumberTernaryOp::InplacePower => self.inplace_power.load(), } } pub fn right_ternary_op(&self, op_slot: PyNumberTernaryOp) -> Option { - use PyNumberTernaryOp::*; - match op_slot { - Power => self.right_power.load(), - _ => None, + if op_slot == PyNumberTernaryOp::Power { + self.right_power.load() + } else { + None } } } @@ -602,6 +633,7 @@ impl Deref for PyNumber<'_> { impl<'a> PyNumber<'a> { // PyNumber_Check - slots are now inherited + #[must_use] pub fn check(obj: &PyObject) -> bool { let methods = &obj.class().slots.as_number; let has_number = methods.int.load().is_some() @@ -629,16 +661,12 @@ impl PyNumber<'_> { let ret_class = ret.class().to_owned(); if let Some(ret) = ret.downcast_ref::() { - _warnings::warn( - vm.ctx.exceptions.deprecation_warning, - format!( - "__int__ returned non-int (type {ret_class}). \ - The ability to return an instance of a strict subclass of int \ - is deprecated, and may be removed in a future version of Python." - ), - 1, - vm, - )?; + let msg = format!( + "__int__ returned non-int (type {ret_class}). \ +The ability to return an instance of a strict subclass of int is deprecated, \ +and may be removed in a future version of Python." + ); + _warnings::warn(vm.ctx.exceptions.deprecation_warning, msg, 1, vm)?; Ok(ret.to_owned()) } else { @@ -662,16 +690,12 @@ impl PyNumber<'_> { let ret_class = ret.class().to_owned(); if let Some(ret) = ret.downcast_ref::() { - _warnings::warn( - vm.ctx.exceptions.deprecation_warning, - format!( - "__index__ returned non-int (type {ret_class}). \ - The ability to return an instance of a strict subclass of int \ - is deprecated, and may be removed in a future version of Python." - ), - 1, - vm, - )?; + let msg = format!( + "__index__ returned non-int (type {ret_class}). \ +The ability to return an instance of a strict subclass of int is deprecated, \ +and may be removed in a future version of Python." + ); + _warnings::warn(vm.ctx.exceptions.deprecation_warning, msg, 1, vm)?; Ok(ret.to_owned()) } else { @@ -695,16 +719,12 @@ impl PyNumber<'_> { let ret_class = ret.class().to_owned(); if let Some(ret) = ret.downcast_ref::() { - _warnings::warn( - vm.ctx.exceptions.deprecation_warning, - format!( - "__float__ returned non-float (type {ret_class}). \ - The ability to return an instance of a strict subclass of float \ - is deprecated, and may be removed in a future version of Python." - ), - 1, - vm, - )?; + let msg = format!( + "__float__ returned non-float (type {ret_class}). \ +The ability to return an instance of a strict subclass of float is deprecated, \ +and may be removed in a future version of Python." + ); + _warnings::warn(vm.ctx.exceptions.deprecation_warning, msg, 1, vm)?; Ok(ret.to_owned()) } else { @@ -724,18 +744,22 @@ pub fn handle_bytes_to_int_err( vm: &VirtualMachine, ) -> PyBaseExceptionRef { match e { - BytesToIntError::InvalidLiteral { base } => vm.new_value_error(format!( - "invalid literal for int() with base {base}: {}", - match obj.repr(vm) { + BytesToIntError::InvalidLiteral { base } => { + let v = match obj.repr(vm) { Ok(v) => v, Err(err) => return err, - }, - )), + }; + vm.new_value_error(format!("invalid literal for int() with base {base}: {v}")) + } BytesToIntError::InvalidBase => { vm.new_value_error("int() base must be >= 2 and <= 36, or 0") } - BytesToIntError::DigitLimit { got, limit } => vm.new_value_error(format!( -"Exceeds the limit ({limit} digits) for integer string conversion: value has {got} digits; use sys.set_int_max_str_digits() to increase the limit" - )), + BytesToIntError::DigitLimit { got, limit } => { + let msg = format!( + "Exceeds the limit ({limit} digits) for integer string conversion: \ +value has {got} digits; use sys.set_int_max_str_digits() to increase the limit" + ); + vm.new_value_error(msg) + } } } diff --git a/crates/vm/src/protocol/object.rs b/crates/vm/src/protocol/object.rs index 0d4e206ec19..816390feace 100644 --- a/crates/vm/src/protocol/object.rs +++ b/crates/vm/src/protocol/object.rs @@ -1,5 +1,4 @@ -//! Object Protocol -//! +//! [Object Protocol](https://docs.python.org/3/c-api/object.html) use crate::{ AsObject, Py, PyObject, PyObjectRef, PyRef, PyResult, TryFromObject, VirtualMachine, @@ -33,13 +32,13 @@ impl PyObjectRef { pub fn bytes(self, vm: &VirtualMachine) -> PyResult { let bytes_type = vm.ctx.types.bytes_type; - match self.downcast_exact::(vm) { - Ok(int) => Err(vm.new_downcast_type_error(bytes_type, &int)), - Err(obj) => { + self.downcast_exact::(vm).map_or_else( + |obj| { let args = FuncArgs::from(vec![obj]); ::slot_new(bytes_type.to_owned(), args, vm) - } - } + }, + |int| Err(vm.new_downcast_type_error(bytes_type, &int)), + ) } // const hash_not_implemented: fn(&PyObject, &VirtualMachine) ->PyResult = crate::types::Unhashable::slot_hash; @@ -70,7 +69,7 @@ impl PyObjectRef { )?; } - let attributes: Vec<_> = dict.into_iter().map(|(k, _v)| k).collect(); + let attributes = dict.into_iter().map(|(k, _v)| k).collect::>(); Ok(PyList::from(attributes)) } @@ -103,10 +102,8 @@ impl PyObject { // Check that __aiter__ did not return a coroutine if iterator.downcast_ref::().is_some() { - return Err(vm.new_type_error( - "'async_iterator' object cannot be interpreted as an async iterable; \ - perhaps you forgot to call aiter()?", - )); + const MSG: &str = "'async_iterator' object cannot be interpreted as an async iterable; perhaps you forgot to call aiter()?"; + return Err(vm.new_type_error(MSG)); } // Check that the result is an async iterator (has __anext__) @@ -318,6 +315,7 @@ impl PyObject { let other_class = other.class(); !self_class.is(other_class) && other_class.fast_issubclass(self_class) }; + if is_strict_subclass { let res = call_cmp(other, self, swapped)?; checked_reverse_op = true; @@ -325,15 +323,18 @@ impl PyObject { return Ok(x); } } + if let PyArithmeticValue::Implemented(x) = call_cmp(self, other, op)? { return Ok(x); } + if !checked_reverse_op { let res = call_cmp(other, self, swapped)?; if let PyArithmeticValue::Implemented(x) = res { return Ok(x); } } + match op { PyComparisonOp::Eq => Ok(Either::B(self.is(&other))), PyComparisonOp::Ne => Ok(Either::B(!self.is(&other))), @@ -345,6 +346,7 @@ impl PyObject { ))), } } + #[inline(always)] pub fn rich_compare_bool( &self, @@ -366,6 +368,7 @@ impl PyObject { _ => {} } } + match self._cmp(other, op_id, vm)? { Either::A(obj) => obj.try_to_bool(vm), Either::B(other) => Ok(other), @@ -392,21 +395,23 @@ impl PyObject { pub fn ascii(&self, vm: &VirtualMachine) -> PyResult> { let repr = self.repr(vm)?; - if repr.as_wtf8().is_ascii() { - Ok(repr) + Ok(if repr.as_wtf8().is_ascii() { + repr } else { - Ok(vm.ctx.new_str(to_ascii(repr.as_wtf8()))) - } + vm.ctx.new_str(to_ascii(repr.as_wtf8())) + }) } pub fn str_utf8(&self, vm: &VirtualMachine) -> PyResult> { self.str(vm)?.try_into_utf8(vm) } + pub fn str(&self, vm: &VirtualMachine) -> PyResult> { let obj = match self.to_owned().downcast_exact::(vm) { Ok(s) => return Ok(s.into_pyref()), Err(obj) => obj, }; + // Fast path for exact int: skip __str__ method resolution let obj = match obj.downcast_exact::(vm) { Ok(int) => { @@ -415,11 +420,12 @@ impl PyObject { } Err(obj) => obj, }; + // TODO: replace to obj.class().slots.str - let str_method = match vm.get_special_method(&obj, identifier!(vm, __str__))? { - Some(str_method) => str_method, - None => return obj.repr(vm), + let Some(str_method) = vm.get_special_method(&obj, identifier!(vm, __str__))? else { + return obj.repr(vm); }; + let s = str_method.invoke((), vm)?; s.downcast::().map_err(|obj| { vm.new_type_error(format!( @@ -435,13 +441,12 @@ impl PyObject { where F: Fn() -> String, { - let cls = self; - match cls.abstract_get_bases(vm)? { - Some(_bases) => Ok(()), // Has __bases__, it's a valid class - None => { - // No __bases__ or __bases__ is not a tuple - Err(vm.new_type_error(msg())) - } + if self.abstract_get_bases(vm)?.is_some() { + // Has __bases__, it's a valid class + Ok(()) + } else { + // No __bases__ or __bases__ is not a tuple + Err(vm.new_type_error(msg())) } } @@ -455,16 +460,13 @@ impl PyObject { /// If an object other than a tuple comes out of __bases__, then again, None is returned. /// Other exceptions are propagated. fn abstract_get_bases(&self, vm: &VirtualMachine) -> PyResult> { - match vm.get_attribute_opt(self.to_owned(), identifier!(vm, __bases__))? { - Some(bases) => { + Ok(vm + .get_attribute_opt(self.to_owned(), identifier!(vm, __bases__))? + // If we get `None` then AttributeError was masked. + .and_then(|bases| { // Check if it's a tuple - match PyTupleRef::try_from_object(vm, bases) { - Ok(tuple) => Ok(Some(tuple)), - Err(_) => Ok(None), // Not a tuple, return None - } - } - None => Ok(None), // AttributeError was masked - } + PyTupleRef::try_from_object(vm, bases).ok() + })) } fn abstract_issubclass(&self, cls: &Self, vm: &VirtualMachine) -> PyResult { @@ -507,6 +509,7 @@ impl PyObject { let result = vm.with_recursion("in __issubclass__", || { bases.as_slice()[i].abstract_issubclass(cls, vm) })?; + if result { return Ok(true); } @@ -523,6 +526,7 @@ impl PyObject { // PyType_IsSubtype equivalent return Ok(derived.is_subtype(cls)); } + // Check if derived is a class self.check_class(vm, || { format!("issubclass() arg 1 must be a class, not {}", self.class()) @@ -693,10 +697,7 @@ impl PyObject { return hash(self, vm); } - Err(vm.new_exception_msg( - vm.ctx.exceptions.type_error.to_owned(), - format!("unhashable type: '{}'", self.class().name()).into(), - )) + Err(vm.new_type_error(format!("unhashable type: '{}'", self.class().name()))) } // type protocol diff --git a/crates/vm/src/protocol/sequence.rs b/crates/vm/src/protocol/sequence.rs index 8a77d5a901d..dbef92c66a9 100644 --- a/crates/vm/src/protocol/sequence.rs +++ b/crates/vm/src/protocol/sequence.rs @@ -1,3 +1,8 @@ +//! [Sequence Protocol](https://docs.python.org/3/c-api/sequence.html) + +use crossbeam_utils::atomic::AtomicCell; +use itertools::Itertools; + use crate::{ AsObject, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine, builtins::{PyList, PyListRef, PySlice, PyTuple, PyTupleRef}, @@ -6,13 +11,8 @@ use crate::{ object::{Traverse, TraverseFn}, protocol::PyNumberBinaryOp, }; -use crossbeam_utils::atomic::AtomicCell; -use itertools::Itertools; - -// Sequence Protocol -// https://docs.python.org/3/c-api/sequence.html -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] #[derive(Default)] pub struct PySequenceSlots { pub length: AtomicCell, &VirtualMachine) -> PyResult>>, @@ -45,31 +45,38 @@ impl PySequenceSlots { if let Some(f) = methods.length { self.length.store(Some(f)); } + if let Some(f) = methods.concat { self.concat.store(Some(f)); } + if let Some(f) = methods.repeat { self.repeat.store(Some(f)); } + if let Some(f) = methods.item { self.item.store(Some(f)); } + if let Some(f) = methods.ass_item { self.ass_item.store(Some(f)); } + if let Some(f) = methods.contains { self.contains.store(Some(f)); } + if let Some(f) = methods.inplace_concat { self.inplace_concat.store(Some(f)); } + if let Some(f) = methods.inplace_repeat { self.inplace_repeat.store(Some(f)); } } } -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] #[derive(Default)] pub struct PySequenceMethods { pub length: Option, &VirtualMachine) -> PyResult>, @@ -104,7 +111,7 @@ impl PySequenceMethods { impl PyObject { #[inline] - pub fn sequence_unchecked(&self) -> PySequence<'_> { + pub const fn sequence_unchecked(&self) -> PySequence<'_> { PySequence { obj: self } } @@ -215,6 +222,7 @@ impl PySequence<'_> { if let Some(f) = self.slots().inplace_repeat.load() { return f(self, n, vm); } + if let Some(f) = self.slots().repeat.load() { return f(self, n, vm); } @@ -233,6 +241,7 @@ impl PySequence<'_> { if let Some(f) = self.slots().item.load() { return f(self, i, vm); } + Err(vm.new_type_error(format!( "'{}' is not a sequence or does not support indexing", self.obj.class() @@ -243,6 +252,7 @@ impl PySequence<'_> { if let Some(f) = self.slots().ass_item.load() { return f(self, i, value, vm); } + Err(vm.new_type_error(format!( "'{}' is not a sequence or doesn't support item {}", self.obj.class(), @@ -330,8 +340,7 @@ impl PySequence<'_> { } pub fn list(&self, vm: &VirtualMachine) -> PyResult { - let list = vm.ctx.new_list(self.obj.try_to_value(vm)?); - Ok(list) + Ok(vm.ctx.new_list(self.obj.try_to_value(vm)?)) } pub fn count(&self, target: &PyObject, vm: &VirtualMachine) -> PyResult { @@ -354,20 +363,17 @@ impl PySequence<'_> { } pub fn index(&self, target: &PyObject, vm: &VirtualMachine) -> PyResult { - let mut index: isize = -1; - let iter = self.obj.to_owned().get_iter(vm)?; let iter = iter.iter::(vm)?; - for elem in iter { - if index == isize::MAX { + for (index, elem) in iter.enumerate() { + if isize::try_from(index).is_err() { return Err(vm.new_overflow_error("index exceeds C integer size")); } - index += 1; let elem = elem?; if vm.bool_eq(&elem, target)? { - return Ok(index as usize); + return Ok(index); } } diff --git a/crates/vm/src/stdlib/_functools.rs b/crates/vm/src/stdlib/_functools.rs index 3007a4d12a0..c496bd32b32 100644 --- a/crates/vm/src/stdlib/_functools.rs +++ b/crates/vm/src/stdlib/_functools.rs @@ -44,11 +44,7 @@ mod _functools { } else { // initial was not provided at all iter.next().transpose()?.ok_or_else(|| { - let exc_type = vm.ctx.exceptions.type_error.to_owned(); - vm.new_exception_msg( - exc_type, - "reduce() of empty sequence with no initial value".into(), - ) + vm.new_type_error("reduce() of empty sequence with no initial value") })? }; From 51cc36e320ff8b2e5fb65d9af903f80fac150f4a Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sun, 7 Jun 2026 18:15:56 +0300 Subject: [PATCH 2/3] fix error message --- crates/vm/src/stdlib/_functools.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/vm/src/stdlib/_functools.rs b/crates/vm/src/stdlib/_functools.rs index c496bd32b32..7c6914c2fb4 100644 --- a/crates/vm/src/stdlib/_functools.rs +++ b/crates/vm/src/stdlib/_functools.rs @@ -44,7 +44,7 @@ mod _functools { } else { // initial was not provided at all iter.next().transpose()?.ok_or_else(|| { - vm.new_type_error("reduce() of empty sequence with no initial value") + vm.new_type_error("reduce() of empty iterable with no initial value") })? }; From 11305fa12662069025c76b03afd71c9a706e2492 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sun, 7 Jun 2026 19:43:45 +0300 Subject: [PATCH 3/3] Use `vm.new_.*_error` methods --- crates/stdlib/src/array.rs | 5 +- crates/stdlib/src/multiprocessing.rs | 10 +-- crates/stdlib/src/ssl.rs | 5 +- crates/vm/src/builtins/int.rs | 5 +- crates/vm/src/builtins/interpolation.rs | 5 +- crates/vm/src/frame.rs | 98 +++++++++---------------- crates/vm/src/stdlib/_abc.rs | 5 +- crates/vm/src/stdlib/_ctypes.rs | 7 +- crates/vm/src/stdlib/marshal.rs | 5 +- crates/vm/src/vm/vm_new.rs | 2 + src/lib.rs | 7 +- 11 files changed, 54 insertions(+), 100 deletions(-) diff --git a/crates/stdlib/src/array.rs b/crates/stdlib/src/array.rs index f3cd601c771..e5f8e11a0a0 100644 --- a/crates/stdlib/src/array.rs +++ b/crates/stdlib/src/array.rs @@ -882,10 +882,7 @@ pub mod array { self._from_bytes(b.as_bytes(), itemsize, vm)?; if not_enough_bytes { - Err(vm.new_exception_msg( - vm.ctx.exceptions.eof_error.to_owned(), - "read() didn't return enough bytes".into(), - )) + Err(vm.new_eof_error("read() didn't return enough bytes")) } else { Ok(()) } diff --git a/crates/stdlib/src/multiprocessing.rs b/crates/stdlib/src/multiprocessing.rs index 53f6692577e..231ca44de1f 100644 --- a/crates/stdlib/src/multiprocessing.rs +++ b/crates/stdlib/src/multiprocessing.rs @@ -175,9 +175,8 @@ mod _multiprocessing { fn release(&self, vm: &VirtualMachine) -> PyResult<()> { if self.kind == RECURSIVE_MUTEX { if !ismine!(self) { - return Err(vm.new_exception_msg( - vm.ctx.exceptions.assertion_error.to_owned(), - "attempt to release recursive lock not owned by thread".into(), + return Err(vm.new_assertion_error( + "attempt to release recursive lock not owned by thread", )); } if self.count.load(Ordering::Acquire) > 1 { @@ -597,9 +596,8 @@ mod _multiprocessing { if self.kind == RECURSIVE_MUTEX { // if (!ISMINE(self)) if !ismine!(self) { - return Err(vm.new_exception_msg( - vm.ctx.exceptions.assertion_error.to_owned(), - "attempt to release recursive lock not owned by thread".into(), + return Err(vm.new_assertion_error( + "attempt to release recursive lock not owned by thread", )); } // if (self->count > 1) { --self->count; Py_RETURN_NONE; } diff --git a/crates/stdlib/src/ssl.rs b/crates/stdlib/src/ssl.rs index d36481a0062..ca2546bfbbe 100644 --- a/crates/stdlib/src/ssl.rs +++ b/crates/stdlib/src/ssl.rs @@ -4657,10 +4657,7 @@ mod _ssl { // It's a memoryview, check if contiguous let is_contiguous: bool = mem_view.try_to_bool(vm)?; if !is_contiguous { - return Err(vm.new_exception_msg( - vm.ctx.exceptions.buffer_error.to_owned(), - "non-contiguous buffer is not supported".into(), - )); + return Err(vm.new_buffer_error("non-contiguous buffer is not supported")); } } diff --git a/crates/vm/src/builtins/int.rs b/crates/vm/src/builtins/int.rs index 5468a011c15..198c2765cdc 100644 --- a/crates/vm/src/builtins/int.rs +++ b/crates/vm/src/builtins/int.rs @@ -236,10 +236,7 @@ fn inner_truediv(i1: &BigInt, i2: &BigInt, vm: &VirtualMachine) -> PyResult { let float = true_div(i1, i2); if float.is_infinite() { - Err(vm.new_exception_msg( - vm.ctx.exceptions.overflow_error.to_owned(), - "integer division result too large for a float".into(), - )) + Err(vm.new_overflow_error("integer division result too large for a float")) } else { Ok(vm.ctx.new_float(float).into()) } diff --git a/crates/vm/src/builtins/interpolation.rs b/crates/vm/src/builtins/interpolation.rs index 8ce17149379..a865ff390de 100644 --- a/crates/vm/src/builtins/interpolation.rs +++ b/crates/vm/src/builtins/interpolation.rs @@ -46,9 +46,8 @@ impl PyInterpolation { .downcast_ref::() .is_some_and(|s| matches!(s.to_str(), Some("s" | "r" | "a"))); if !is_valid { - return Err(vm.new_exception_msg( - vm.ctx.exceptions.system_error.to_owned(), - "Interpolation() argument 'conversion' must be one of 's', 'a' or 'r'".into(), + return Err(vm.new_system_error( + "Interpolation() argument 'conversion' must be one of 's', 'a' or 'r'", )); } Ok(Self { diff --git a/crates/vm/src/frame.rs b/crates/vm/src/frame.rs index f1ed31d7189..6d2782439f2 100644 --- a/crates/vm/src/frame.rs +++ b/crates/vm/src/frame.rs @@ -2038,10 +2038,9 @@ impl ExecutingFrame<'_> { } else { // Both merged cells (LOCAL|CELL) and non-merged cells get unbound local error let name = self.localsplus_name(localsplus_idx); - vm.new_exception_msg( - vm.ctx.exceptions.unbound_local_error.to_owned(), - format!("local variable '{name}' referenced before assignment").into(), - ) + vm.new_unbound_local_error(format!( + "local variable '{name}' referenced before assignment" + )) } } @@ -2378,14 +2377,10 @@ impl ExecutingFrame<'_> { let fastlocals = self.localsplus.fastlocals_mut(); let idx = var_num.get(arg); if fastlocals[idx].is_none() { - return Err(vm.new_exception_msg( - vm.ctx.exceptions.unbound_local_error.to_owned(), - format!( - "local variable '{}' referenced before assignment", - self.code.varnames[idx] - ) - .into(), - )); + return Err(vm.new_unbound_local_error(format!( + "local variable '{}' referenced before assignment", + self.code.varnames[idx] + ))); } fastlocals[idx] = None; Ok(None) @@ -2884,10 +2879,9 @@ impl ExecutingFrame<'_> { varname: &'static PyStrInterned, vm: &VirtualMachine, ) -> PyBaseExceptionRef { - vm.new_exception_msg( - vm.ctx.exceptions.unbound_local_error.to_owned(), - format!("local variable '{varname}' referenced before assignment").into(), - ) + vm.new_unbound_local_error(format!( + "local variable '{varname}' referenced before assignment" + )) } let idx = var_num.get(arg); let x = self.localsplus.fastlocals()[idx] @@ -2910,14 +2904,10 @@ impl ExecutingFrame<'_> { // (LoadFast in RustPython already does this check) let idx = var_num.get(arg); let x = self.localsplus.fastlocals()[idx].clone().ok_or_else(|| { - vm.new_exception_msg( - vm.ctx.exceptions.unbound_local_error.to_owned(), - format!( - "local variable '{}' referenced before assignment", - self.code.varnames[idx] - ) - .into(), - ) + vm.new_unbound_local_error(format!( + "local variable '{}' referenced before assignment", + self.code.varnames[idx] + )) })?; self.push_value(x); Ok(None) @@ -2929,24 +2919,16 @@ impl ExecutingFrame<'_> { let (idx1, idx2) = oparg.indexes(); let fastlocals = self.localsplus.fastlocals(); let x1 = fastlocals[idx1].clone().ok_or_else(|| { - vm.new_exception_msg( - vm.ctx.exceptions.unbound_local_error.to_owned(), - format!( - "local variable '{}' referenced before assignment", - self.code.varnames[idx1] - ) - .into(), - ) + vm.new_unbound_local_error(format!( + "local variable '{}' referenced before assignment", + self.code.varnames[idx1] + )) })?; let x2 = fastlocals[idx2].clone().ok_or_else(|| { - vm.new_exception_msg( - vm.ctx.exceptions.unbound_local_error.to_owned(), - format!( - "local variable '{}' referenced before assignment", - self.code.varnames[idx2] - ) - .into(), - ) + vm.new_unbound_local_error(format!( + "local variable '{}' referenced before assignment", + self.code.varnames[idx2] + )) })?; self.push_value(x1); self.push_value(x2); @@ -2958,14 +2940,10 @@ impl ExecutingFrame<'_> { Instruction::LoadFastBorrow { var_num } => { let idx = var_num.get(arg); let x = self.localsplus.fastlocals()[idx].clone().ok_or_else(|| { - vm.new_exception_msg( - vm.ctx.exceptions.unbound_local_error.to_owned(), - format!( - "local variable '{}' referenced before assignment", - self.code.varnames[idx] - ) - .into(), - ) + vm.new_unbound_local_error(format!( + "local variable '{}' referenced before assignment", + self.code.varnames[idx] + )) })?; self.push_value(x); Ok(None) @@ -2975,24 +2953,16 @@ impl ExecutingFrame<'_> { let (idx1, idx2) = oparg.indexes(); let fastlocals = self.localsplus.fastlocals(); let x1 = fastlocals[idx1].clone().ok_or_else(|| { - vm.new_exception_msg( - vm.ctx.exceptions.unbound_local_error.to_owned(), - format!( - "local variable '{}' referenced before assignment", - self.code.varnames[idx1] - ) - .into(), - ) + vm.new_unbound_local_error(format!( + "local variable '{}' referenced before assignment", + self.code.varnames[idx1] + )) })?; let x2 = fastlocals[idx2].clone().ok_or_else(|| { - vm.new_exception_msg( - vm.ctx.exceptions.unbound_local_error.to_owned(), - format!( - "local variable '{}' referenced before assignment", - self.code.varnames[idx2] - ) - .into(), - ) + vm.new_unbound_local_error(format!( + "local variable '{}' referenced before assignment", + self.code.varnames[idx2] + )) })?; self.push_value(x1); self.push_value(x2); diff --git a/crates/vm/src/stdlib/_abc.rs b/crates/vm/src/stdlib/_abc.rs index f9906956f9a..6cdef861253 100644 --- a/crates/vm/src/stdlib/_abc.rs +++ b/crates/vm/src/stdlib/_abc.rs @@ -361,9 +361,8 @@ mod _abc { return Ok(false); } if !ok.is(&vm.ctx.not_implemented) { - return Err(vm.new_exception_msg( - vm.ctx.exceptions.assertion_error.to_owned(), - "__subclasshook__ must return either False, True, or NotImplemented".into(), + return Err(vm.new_assertion_error( + "__subclasshook__ must return either False, True, or NotImplemented", )); } diff --git a/crates/vm/src/stdlib/_ctypes.rs b/crates/vm/src/stdlib/_ctypes.rs index cc87ebde572..6370bc42b3d 100644 --- a/crates/vm/src/stdlib/_ctypes.rs +++ b/crates/vm/src/stdlib/_ctypes.rs @@ -69,10 +69,9 @@ impl PyType { if let Some(stg_info) = self.get_type_data::() && stg_info.initialized { - return Err(vm.new_exception_msg( - vm.ctx.exceptions.system_error.to_owned(), - format!("class \"{}\" already initialized", self.name()).into(), - )); + return Err( + vm.new_system_error(format!(r#"class "{}" already initialized"#, self.name())) + ); } Ok(()) } diff --git a/crates/vm/src/stdlib/marshal.rs b/crates/vm/src/stdlib/marshal.rs index 6e0fc4e7f5d..cb43a38bd50 100644 --- a/crates/vm/src/stdlib/marshal.rs +++ b/crates/vm/src/stdlib/marshal.rs @@ -504,10 +504,7 @@ mod decl { let result = marshal::deserialize_value(&mut &buf[..], PyMarshalBag(vm)).map_err(|e| match e { - marshal::MarshalError::Eof => vm.new_exception_msg( - vm.ctx.exceptions.eof_error.to_owned(), - "marshal data too short".into(), - ), + marshal::MarshalError::Eof => vm.new_eof_error("marshal data too short"), _ => vm.new_value_error("bad marshal data"), })?; if !allow_code { diff --git a/crates/vm/src/vm/vm_new.rs b/crates/vm/src/vm/vm_new.rs index 3b50c25695f..9e6e9870445 100644 --- a/crates/vm/src/vm/vm_new.rs +++ b/crates/vm/src/vm/vm_new.rs @@ -922,4 +922,6 @@ impl VirtualMachine { define_exception_fn!(fn new_runtime_error, runtime_error, RuntimeError); define_exception_fn!(fn new_python_finalization_error, python_finalization_error, PythonFinalizationError); define_exception_fn!(fn new_memory_error, memory_error, MemoryError); + define_exception_fn!(fn new_assertion_error, assertion_error, AssertionError); + define_exception_fn!(fn new_unbound_local_error, unbound_local_error, UnboundLocalError); } diff --git a/src/lib.rs b/src/lib.rs index 14deb2972d4..d7dc6255e41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -152,10 +152,9 @@ __import__("io").TextIOWrapper( fn install_pip(installer: InstallPipMode, scope: Scope, vm: &VirtualMachine) -> PyResult<()> { if !cfg!(feature = "ssl") { - return Err(vm.new_exception_msg( - vm.ctx.exceptions.system_error.to_owned(), - "install-pip requires rustpython be build with '--features=ssl'".into(), - )); + return Err( + vm.new_system_error("install-pip requires rustpython be build with '--features=ssl'") + ); } match installer {