Skip to content

Commit 1ad9beb

Browse files
authored
Accept any index as the operator.length_hint() default (#9515)
length_hint() required its default to be an exact int and converted it to usize, so a default with __index__ raised TypeError and a negative default raised ValueError. Bind the default as isize, which goes through __index__, and return it unchanged when the object has no length hint. Assisted-by: Claude Code:claude-opus-5-5
1 parent 4591756 commit 1ad9beb

2 files changed

Lines changed: 31 additions & 15 deletions

File tree

‎crates/vm/src/stdlib/_operator.rs‎

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pub(crate) use _operator::module_def;
44
mod _operator {
55
use crate::{
66
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
7-
builtins::{PyInt, PyIntRef, PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef, PyUtf8StrRef},
7+
builtins::{PyIntRef, PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef, PyUtf8StrRef},
88
class::PyClassDef,
99
common::wtf8::{Wtf8, Wtf8Buf},
1010
function::{ArgBytesLike, ArgumentError, Either, FromArgs, FuncArgs, Param},
@@ -223,25 +223,16 @@ mod _operator {
223223
struct LengthHintArgs {
224224
#[pyarg(positional)]
225225
obj: PyObjectRef,
226-
// The value is checked as an exact int.
227226
#[pyarg(positional, default = 0)]
228-
default: PyObjectRef,
227+
default: isize,
229228
}
230229

231230
#[pyfunction]
232-
fn length_hint(args: LengthHintArgs, vm: &VirtualMachine) -> PyResult<usize> {
231+
fn length_hint(args: LengthHintArgs, vm: &VirtualMachine) -> PyResult<isize> {
233232
let LengthHintArgs { obj, default } = args;
234-
if !default.fast_isinstance(vm.ctx.types.int_type) {
235-
return Err(vm.new_type_error(format!(
236-
"'{}' object cannot be interpreted as an integer",
237-
default.class().name()
238-
)));
239-
}
240-
let default: usize = default
241-
.downcast_ref::<PyInt>()
242-
.unwrap()
243-
.try_to_primitive(vm)?;
244-
obj.length_hint(default, vm)
233+
Ok(vm
234+
.length_hint_opt(obj)?
235+
.map_or(default, |hint| hint as isize))
245236
}
246237

247238
// Inplace Operators

‎extra_tests/snippets/stdlib_operator.py‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import _operator
22

3+
from testutils import assert_raises
4+
35
assert _operator._compare_digest("abcdef", "abcdef")
46
assert not _operator._compare_digest("abcdef", "abc")
57
assert not _operator._compare_digest("abc", "abcdef")
@@ -31,3 +33,26 @@ class Name(str):
3133

3234

3335
check_operator_reference_cycles()
36+
37+
38+
class Index:
39+
def __index__(self):
40+
return 5
41+
42+
43+
class RaisingIndex:
44+
def __index__(self):
45+
raise ZeroDivisionError
46+
47+
48+
class TupleHint:
49+
def __length_hint__(self):
50+
return ()
51+
52+
53+
assert _operator.length_hint(object(), Index()) == 5
54+
assert_raises(ZeroDivisionError, _operator.length_hint, [], RaisingIndex())
55+
assert _operator.length_hint(object(), -1) == -1
56+
assert _operator.length_hint([], -1) == 0
57+
assert_raises(TypeError, _operator.length_hint, TupleHint(), -1)
58+
assert_raises(OverflowError, _operator.length_hint, object(), -(2**70))

0 commit comments

Comments
 (0)