Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Fold str's subscript TypeError into the shared index helper
The previous commit added `SequenceIndex::try_from_borrowed_object_with_err`
(a closure-taking function) so `str` could raise CPython's
"string indices must be integers, not ..." message. Per review, a whole
helper API plus a thin wrapper was overkill for a single caller.

Instead, inline that one error case into `try_from_borrowed_object`: when
`type_name` is "str", raise the str-specific message; every other type keeps
the shared sequence wording. `str._getitem` goes back to the plain `match`
over `SequenceIndex` with no duplicated index parsing. A bad slice key
(e.g. `'a'[::'x']`) still surfaces the slice-specific error, since only the
non-index/non-slice branch is customized.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
  • Loading branch information
kim-jaedeok and claude committed Jul 28, 2026
commit b5b36a822665f26a06d3e234c665e2a31f9146f6
8 changes: 1 addition & 7 deletions crates/vm/src/builtins/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,13 +660,7 @@ impl PyStr {
}

fn _getitem(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult {
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 {
let item = match SequenceIndex::try_from_borrowed_object(vm, needle, "str")? {
SequenceIndex::Int(i) => self.getitem_by_index(vm, i)?.to_pyobject(vm),
SequenceIndex::Slice(slice) => self.getitem_by_slice(vm, slice)?.to_pyobject(vm),
};
Expand Down
32 changes: 12 additions & 20 deletions crates/vm/src/sliceable.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// export through sliceable module, not slice.
use crate::{
PyObject, PyResult, VirtualMachine,
builtins::{PyBaseExceptionRef, int::PyInt, slice::PySlice},
builtins::{int::PyInt, slice::PySlice},
};
use core::ops::Range;
use malachite_bigint::BigInt;
Expand Down Expand Up @@ -262,24 +262,6 @@ impl SequenceIndex {
vm: &VirtualMachine,
obj: &PyObject,
type_name: &str,
) -> PyResult<Self> {
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<Self> {
if let Some(i) = obj.downcast_ref::<PyInt>() {
// TODO: number protocol
Expand All @@ -293,8 +275,18 @@ impl SequenceIndex {
i?.try_to_primitive(vm)
.map_err(|_| vm.new_index_error("cannot fit 'int' into an index-sized integer"))
.map(Self::Int)
} else if type_name == "str" {
// CPython's unicode_subscript raises a distinct message here.
Err(vm.new_type_error(format!(
"string indices must be integers, not '{}'",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

obj.class()
)))
} else {
Err(err())
Err(vm.new_type_error(format!(
"{} indices must be integers or slices or classes that override __index__ operator, not '{}'",
type_name,
obj.class()
)))
}
}
}
Expand Down
Loading