Skip to content

Match CPython's str subscript TypeError message#8319

Open
kim-jaedeok wants to merge 1 commit into
RustPython:mainfrom
kim-jaedeok:str-subscript-typeerror-message
Open

Match CPython's str subscript TypeError message#8319
kim-jaedeok wants to merge 1 commit into
RustPython:mainfrom
kim-jaedeok:str-subscript-typeerror-message

Conversation

@kim-jaedeok

@kim-jaedeok kim-jaedeok commented Jul 18, 2026

Copy link
Copy Markdown

Summary

Make str subscripting with a non-integer index raise the same TypeError
message as CPython, and drop the expectedFailure marker on
string_tests.test_subscript.

Before / after ('abc'['def']):

message
CPython 3.14 string indices must be integers, not 'str'
RustPython (before) str indices must be integers or slices or classes that override __index__ operator, not 'str'
RustPython (after) string indices must be integers, not 'str'

Only the error message differed — indexing itself (int / negative / slice)
already behaved correctly. CPython gives str its own wording in
unicode_subscript (see python/cpython#88276, fixed in 3.11), distinct from the
shared sequence message used by list/tuple/bytes.

Approach

  • Add SequenceIndex::try_from_borrowed_object_with_err, which keeps the shared
    index-parsing logic but lets the caller supply the TypeError raised for a
    non-index object.
  • try_from_borrowed_object now delegates to it with the existing wording, so
    every other sequence caller (list/tuple/bytes/bytearray/array/mmap/
    struct-sequence) is byte-for-byte unchanged.
  • str::_getitem — the single path shared by __getitem__ and the mapping
    subscript slot — supplies the CPython-accurate message.

Scoped intentionally to str only: CPython declined to unify the sequence
messages (python/cpython#75731, rejected), so the other types' wording is left
as-is.

Testing

  • test_str / test_userstring test_subscript now pass (marker removed).
  • Byte-for-byte match with CPython 3.14.6 for str/object/None/float
    indices, and for UserString as receiver.
  • list/tuple/bytes messages verified unchanged; rustpython-vm and
    rustpython-codegen unit tests pass.

Summary by CodeRabbit

  • Bug Fixes
    • Improved error messages for invalid string indices.
    • String indexing with a non-integer value now raises a clear TypeError identifying the invalid type.
    • Standardized handling of invalid sequence indices for more consistent behavior.

`str.__getitem__` with a non-index key raised the shared sequence
wording ("str indices must be integers or slices or classes that
override __index__ operator, not 'x'") instead of CPython's
str-specific "string indices must be integers, not 'x'" (bpo-44110 /
gh-88276, Python 3.11).

Add `SequenceIndex::try_from_borrowed_object_with_err`, which takes a
caller-supplied error closure, and have the existing
`try_from_borrowed_object` delegate to it with the unchanged shared
message. `str._getitem` now supplies the CPython-accurate message,
which flows through both `__getitem__` and the mapping subscript slot.
Other sequence types keep the shared wording.

Removes the expectedFailure marker on
`string_tests.StringLikeTest.test_subscript`, fixing it for both
test_str and test_userstring.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 18, 2026 18:54
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

SequenceIndex now accepts caller-provided invalid-index errors. PyStr indexing uses this capability to report non-integer indices with a string-specific TypeError message.

Changes

String index error handling

Layer / File(s) Summary
SequenceIndex error factory
crates/vm/src/sliceable.rs
Adds a caller-supplied error factory for invalid index conversion while retaining the shared default error behavior.
String indexing integration
crates/vm/src/builtins/str.rs
Uses the new helper to raise string indices must be integers, not '<type>' for non-integer indices.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: copilot, rlaisqls

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately describes the main change: aligning str subscript TypeError text with CPython.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

📦 Library Dependencies

The following Lib/ modules were modified. Here are their dependencies:

[ ] test: cpython/Lib/test/test_set.py (TODO: 5)

dependencies:

dependent tests: (no tests depend on set)

[x] lib: cpython/Lib/xml
[x] test: cpython/Lib/test/test_xml_etree.py (TODO: 59)
[x] test: cpython/Lib/test/test_xml_etree_c.py
[x] test: cpython/Lib/test/test_minidom.py (TODO: 20)
[x] test: cpython/Lib/test/test_pulldom.py (TODO: 3)
[ ] test: cpython/Lib/test/test_pyexpat.py (TODO: 49)
[x] test: cpython/Lib/test/test_sax.py (TODO: 31)
[x] test: cpython/Lib/test/test_xml_dom_minicompat.py
[x] test: cpython/Lib/test/test_xml_dom_xmlbuilder.py

dependencies:

  • xml

dependent tests: (34 tests)

  • xml: test_doctest test_minidom test_pulldom test_pydoc test_pyexpat test_regrtest test_sax test_typing test_xml_dom_minicompat test_xml_dom_xmlbuilder test_xml_etree
    • plistlib: test_plistlib
      • platform: test__locale test__osx_support test_asyncio test_baseexception test_builtin test_cmath test_ctypes test_fcntl test_math test_mimetypes test_os test_platform test_posix test_shutil test_socket test_ssl test_strptime test_sysconfig test_time test_winreg test_wsgiref
    • xmlrpc.client: test_xmlrpc

[x] lib: cpython/Lib/csv.py
[x] test: cpython/Lib/test/test_csv.py (TODO: 24)

dependencies:

  • csv

dependent tests: (4 tests)

  • csv: test_csv test_genericalias
    • importlib.metadata: test_importlib test_zoneinfo

[x] lib: cpython/Lib/test/libregrtest

dependencies:

  • regrtest

dependent tests: (no tests depend on regrtest)

[x] lib: cpython/Lib/weakref.py
[x] lib: cpython/Lib/_weakrefset.py
[x] test: cpython/Lib/test/test_weakref.py (TODO: 13)
[x] test: cpython/Lib/test/test_weakset.py

dependencies:

  • weakref

dependent tests: (222 tests)

  • weakref: test_array test_ast test_asyncio test_code test_concurrent_futures test_context test_contextlib test_copy test_ctypes test_deque test_descr test_dict test_enum test_exceptions test_file test_fileio test_finalization test_frame test_functools test_gc test_generators test_genericalias test_importlib test_inspect test_io test_ipaddress test_itertools test_logging test_memoryio test_memoryview test_mmap test_ordered_dict test_pickle test_picklebuffer test_queue test_re test_scope test_set test_slice test_socket test_sqlite3 test_ssl test_struct test_sys test_tempfile test_thread test_threading test_threading_local test_type_params test_types test_typing test_unittest test_uuid test_weakref test_weakset test_xml_etree
    • asyncio: test_asyncio test_external_inspection test_os test_pdb test_unittest
    • bdb: test_bdb
    • concurrent.futures.process: test_compileall test_concurrent_futures
    • copy: test_bytes test_codecs test_collections test_copyreg test_coroutines test_csv test_decimal test_defaultdict test_dictviews test_email test_fractions test_http_cookies test_minidom test_opcache test_optparse test_platform test_plistlib test_posix test_site test_statistics test_structseq test_super test_sysconfig test_tomllib test_urllib2 test_xml_dom_minicompat test_zlib
      • argparse: test_argparse
      • collections: test_annotationlib test_bisect test_builtin test_c_locale_coercion test_call test_configparser test_contains test_ctypes test_embed test_exception_group test_fileinput test_funcattrs test_hash test_httpservers test_iter test_iterlen test_json test_math test_monitoring test_pathlib test_patma test_pprint test_pydoc test_random test_reprlib test_richcmp test_shelve test_sqlite3 test_string test_traceback test_tuple test_urllib test_userdict test_userlist test_userstring test_with
      • dataclasses: test__colorize test_ctypes test_regrtest test_zoneinfo
      • email.generator: test_email
      • gettext: test_gettext test_tools
      • http.cookiejar: test_http_cookiejar
      • http.server: test_robotparser test_urllib2_localnet test_xmlrpc
      • logging.handlers: test_pkgutil
      • mailbox: test_mailbox
      • smtplib: test_smtplib test_smtpnet
      • tarfile: test_shutil test_tarfile
      • webbrowser: test_webbrowser
    • inspect: test_abc test_asyncgen test_buffer test_clinic test_grammar test_ntpath test_operator test_posixpath test_signal test_turtle test_type_annotations test_yield_from test_zipimport test_zipimport_support
      • ast: test_compile test_compiler_codegen test_dis test_fstring test_future_stmt test_peepholer test_peg_generator test_type_comments test_ucn test_unparse
      • cmd: test_cmd
      • importlib.metadata: test_importlib
      • pkgutil: test_pyrepl test_runpy
      • rlcompleter: test_pyrepl test_rlcompleter
      • trace: test_trace
    • logging: test_hashlib test_support test_urllib2net
      • hashlib: test_hmac test_unicodedata
      • multiprocessing.util: test_concurrent_futures
      • venv: test_venv
    • multiprocessing: test_fcntl test_multiprocessing_main_handling
    • symtable: test_symtable
    • tempfile: test_bz2 test_cmd_line test_cprofile test_ctypes test_doctest test_ensurepip test_faulthandler test_filecmp test_generated_cases test_importlib test_launcher test_linecache test_modulefinder test_peg_generator test_pkg test_pstats test_py_compile test_pyrepl test_selectors test_string_literals test_subprocess test_sys_settrace test_tabnanny test_termios test_threadedtempfile test_tokenize test_urllib_response test_winconsoleio test_zipapp test_zipfile test_zipfile64 test_zstd
      • ctypes.util: test_ctypes
      • urllib.request: test_sax test_urllibnet

Legend:

  • [+] path exists in CPython
  • [x] up-to-date, [ ] outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Aligns RustPython’s str subscripting TypeError wording with CPython by allowing SequenceIndex parsing to reuse the shared index logic while letting callers customize the “non-index object” error message. This removes a now-unnecessary expectedFailure in the CPython-derived string test suite.

Changes:

  • Added SequenceIndex::try_from_borrowed_object_with_err to centralize index parsing while allowing custom TypeError construction for non-index objects.
  • Updated str subscripting to use the CPython-matching message: string indices must be integers, not '<type>'.
  • Removed @unittest.expectedFailure from Lib/test/string_tests.py::test_subscript now that behavior matches CPython.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
Lib/test/string_tests.py Removes expectedFailure marker for test_subscript now that str subscript error message matches CPython.
crates/vm/src/sliceable.rs Introduces try_from_borrowed_object_with_err and refactors the existing helper to delegate, preserving existing messages for other sequence types.
crates/vm/src/builtins/str.rs Uses the new helper to raise the CPython-accurate TypeError message for invalid str indices.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/vm/src/sliceable.rs`:
- Around line 266-272: Use the class name string in both error-formatting sites:
append `.name()` to `obj.class()` in `crates/vm/src/sliceable.rs` lines 266-272
and to `needle.class()` in `crates/vm/src/builtins/str.rs` lines 663-668,
preserving the exact CPython-compatible wording.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: de10d7d7-f5ed-4f7f-a75e-8a9988c893a6

📥 Commits

Reviewing files that changed from the base of the PR and between c7c67fd and 6ecda1b.

⛔ Files ignored due to path filters (1)
  • Lib/test/string_tests.py is excluded by !Lib/**
📒 Files selected for processing (2)
  • crates/vm/src/builtins/str.rs
  • crates/vm/src/sliceable.rs

Comment on lines +266 to +272
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()
))
})

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use .name() to format the class name correctly.

Passing the type object directly to format! may fail to compile or produce an incorrect string representation (like <class 'str'> instead of str, defeating the PR's objective of strictly matching CPython's exact error wording). Consistent with other error formatting in the codebase, call .name() on the class to extract just the type name string.

  • crates/vm/src/sliceable.rs#L266-L272: append .name() to obj.class().
  • crates/vm/src/builtins/str.rs#L663-L668: append .name() to needle.class().
📍 Affects 2 files
  • crates/vm/src/sliceable.rs#L266-L272 (this comment)
  • crates/vm/src/builtins/str.rs#L663-L668
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/vm/src/sliceable.rs` around lines 266 - 272, Use the class name string
in both error-formatting sites: append `.name()` to `obj.class()` in
`crates/vm/src/sliceable.rs` lines 266-272 and to `needle.class()` in
`crates/vm/src/builtins/str.rs` lines 663-668, preserving the exact
CPython-compatible wording.

@moreal moreal added the z-ca-2026 Tag to track Contribution Academy 2026 label Jul 19, 2026

@youknowone youknowone left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👍

pub fn try_from_borrowed_object_with_err(
vm: &VirtualMachine,
obj: &PyObject,
err: impl FnOnce() -> PyBaseExceptionRef,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

because we always raise type error, err can be error message generator rather than a full type error generator.

Suggested change
err: impl FnOnce() -> PyBaseExceptionRef,
err_fmt: impl FnOnce() -> String,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I prefer to use fn function pointer rather than closure in this place if possible, to let it not to be inlined

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

z-ca-2026 Tag to track Contribution Academy 2026

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants