diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py index 0c159e02fb9..66e6d78940b 100644 --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -1301,7 +1301,6 @@ def test___contains__(self): self.checkequal(False, 'asd', '__contains__', 'asdf') self.checkequal(False, '', '__contains__', 'asdf') - @unittest.expectedFailure # TODO: RUSTPYTHON def test_subscript(self): self.checkequal('a', 'abc', '__getitem__', 0) self.checkequal('c', 'abc', '__getitem__', -1) diff --git a/crates/vm/src/builtins/str.rs b/crates/vm/src/builtins/str.rs index bd5cd39ddb0..f80946edd26 100644 --- a/crates/vm/src/builtins/str.rs +++ b/crates/vm/src/builtins/str.rs @@ -660,7 +660,13 @@ impl PyStr { } fn _getitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult { - let item = match SequenceIndex::try_from_borrowed_object(vm, needle, "str")? { + let index = SequenceIndex::try_from_borrowed_object_with_err(vm, needle, || { + vm.new_type_error(format!( + "string indices must be integers, not '{}'", + needle.class() + )) + })?; + let item = match index { SequenceIndex::Int(i) => self.getitem_by_index(vm, i)?.to_pyobject(vm), SequenceIndex::Slice(slice) => self.getitem_by_slice(vm, slice)?.to_pyobject(vm), }; diff --git a/crates/vm/src/sliceable.rs b/crates/vm/src/sliceable.rs index b0f4c7808ff..00d6892392c 100644 --- a/crates/vm/src/sliceable.rs +++ b/crates/vm/src/sliceable.rs @@ -1,7 +1,7 @@ // export through sliceable module, not slice. use crate::{ PyObject, PyResult, VirtualMachine, - builtins::{int::PyInt, slice::PySlice}, + builtins::{PyBaseExceptionRef, int::PyInt, slice::PySlice}, }; use core::ops::Range; use malachite_bigint::BigInt; @@ -262,6 +262,24 @@ impl SequenceIndex { vm: &VirtualMachine, obj: &PyObject, type_name: &str, + ) -> PyResult { + Self::try_from_borrowed_object_with_err(vm, obj, || { + vm.new_type_error(format!( + "{} indices must be integers or slices or classes that override __index__ operator, not '{}'", + type_name, + obj.class() + )) + }) + } + + /// Like [`Self::try_from_borrowed_object`], but lets the caller supply the + /// `TypeError` raised for a non-index object. Used by types whose CPython + /// message differs from the shared sequence wording (e.g. `str`, whose + /// `unicode_subscript` says "string indices must be integers, not ..."). + pub fn try_from_borrowed_object_with_err( + vm: &VirtualMachine, + obj: &PyObject, + err: impl FnOnce() -> PyBaseExceptionRef, ) -> PyResult { if let Some(i) = obj.downcast_ref::() { // TODO: number protocol @@ -276,11 +294,7 @@ impl SequenceIndex { .map_err(|_| vm.new_index_error("cannot fit 'int' into an index-sized integer")) .map(Self::Int) } else { - Err(vm.new_type_error(format!( - "{} indices must be integers or slices or classes that override __index__ operator, not '{}'", - type_name, - obj.class() - ))) + Err(err()) } } }