diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index cc1affc669..3c634b6cac 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -209,8 +209,6 @@ def test_constructor_overflow(self): except (OverflowError, MemoryError): pass - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_constructor_exceptions(self): # Issue #34974: bytes and bytearray constructors replace unexpected # exceptions. diff --git a/vm/src/bytes_inner.rs b/vm/src/bytes_inner.rs index 550c9f8a74..e2fb95a28a 100644 --- a/vm/src/bytes_inner.rs +++ b/vm/src/bytes_inner.rs @@ -21,6 +21,9 @@ use itertools::Itertools; use malachite_bigint::BigInt; use num_traits::ToPrimitive; +const STRING_WITHOUT_ENCODING: &str = "string argument without an encoding"; +const ENCODING_WITHOUT_STRING: &str = "encoding without a string argument"; + #[derive(Debug, Default, Clone)] pub struct PyBytesInner { pub(super) elements: Vec, @@ -75,6 +78,18 @@ impl ByteInnerNewOptions { Ok(vec![0; size].into()) } + fn handle_object_fallback(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { + match_class!(match obj { + i @ PyInt => { + Self::get_value_from_size(i, vm) + } + _s @ PyStr => Err(vm.new_type_error(STRING_WITHOUT_ENCODING.to_owned())), + obj => { + Self::get_value_from_source(obj, vm) + } + }) + } + pub fn get_bytes(self, cls: PyTypeRef, vm: &VirtualMachine) -> PyResult { let inner = match (&self.source, &self.encoding, &self.errors) { (OptionalArg::Present(obj), OptionalArg::Missing, OptionalArg::Missing) => { @@ -113,40 +128,48 @@ impl ByteInnerNewOptions { } pub fn get_bytearray_inner(self, vm: &VirtualMachine) -> PyResult { - const STRING_WITHOUT_ENCODING: &str = "string argument without an encoding"; - const ENCODING_WITHOUT_STRING: &str = "encoding without a string argument"; - match (self.source, self.encoding, self.errors) { (OptionalArg::Present(obj), OptionalArg::Missing, OptionalArg::Missing) => { - match_class!(match obj { - i @ PyInt => { - Ok(Self::get_value_from_size(i, vm)?) - } - _s @ PyStr => Err(STRING_WITHOUT_ENCODING), - obj => { - Ok(Self::get_value_from_source(obj, vm)?) + // Try __index__ first to handle int-like objects that might raise custom exceptions + if let Some(index_result) = obj.try_index_opt(vm) { + match index_result { + Ok(index) => Self::get_value_from_size(index, vm), + Err(e) => { + // Only propagate non-TypeError exceptions + // TypeError means the object doesn't support __index__, so fall back + if e.fast_isinstance(vm.ctx.exceptions.type_error) { + // Fall back to treating as buffer-like object + Self::handle_object_fallback(obj, vm) + } else { + // Propagate other exceptions (e.g., ZeroDivisionError) + Err(e) + } + } } - }) + } else { + Self::handle_object_fallback(obj, vm) + } } (OptionalArg::Present(obj), OptionalArg::Present(encoding), errors) => { if let Ok(s) = obj.downcast::() { - Ok(Self::get_value_from_string(s, encoding, errors, vm)?) + Self::get_value_from_string(s, encoding, errors, vm) } else { - Err(ENCODING_WITHOUT_STRING) + Err(vm.new_type_error(ENCODING_WITHOUT_STRING.to_owned())) } } (OptionalArg::Missing, OptionalArg::Missing, OptionalArg::Missing) => { Ok(PyBytesInner::default()) } - (OptionalArg::Missing, OptionalArg::Present(_), _) => Err(ENCODING_WITHOUT_STRING), + (OptionalArg::Missing, OptionalArg::Present(_), _) => { + Err(vm.new_type_error(ENCODING_WITHOUT_STRING.to_owned())) + } (OptionalArg::Missing, _, OptionalArg::Present(_)) => { - Err("errors without a string argument") + Err(vm.new_type_error("errors without a string argument".to_owned())) } (OptionalArg::Present(_), OptionalArg::Missing, OptionalArg::Present(_)) => { - Err(STRING_WITHOUT_ENCODING) + Err(vm.new_type_error(STRING_WITHOUT_ENCODING.to_owned())) } } - .map_err(|e| vm.new_type_error(e.to_owned())) } }