From bdcc04798e20e480a018ad606e308ca30fc4abf6 Mon Sep 17 00:00:00 2001 From: Elmir Jagudin Date: Thu, 12 Feb 2026 14:36:14 +0100 Subject: [PATCH] raise TypeError when `__iter__` method is `None` Setting special `__iter__` method to `None` is used to mark objects as non-iterable. https://docs.python.org/3/reference/datamodel.html#special-method-names Check if `__iter__` is set to `None` and raise TypeError exception. --- Lib/test/test_typing.py | 1 - crates/vm/src/types/slot.rs | 26 +++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 1038e8c1d1d..4675a1e8875 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -10916,7 +10916,6 @@ class TypeIterationTests(BaseTestCase): Annotated[T, ''], ) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_cannot_iterate(self): expected_error_regex = "object is not iterable" for test_type in self._UNITERABLE_TYPES: diff --git a/crates/vm/src/types/slot.rs b/crates/vm/src/types/slot.rs index 53750610cae..abf24bf03bd 100644 --- a/crates/vm/src/types/slot.rs +++ b/crates/vm/src/types/slot.rs @@ -1,6 +1,7 @@ use crate::common::lock::{ PyMappedRwLockReadGuard, PyMappedRwLockWriteGuard, PyRwLockReadGuard, PyRwLockWriteGuard, }; +use crate::vm::PyMethod; use crate::{ AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, builtins::{PyInt, PyStr, PyStrInterned, PyStrRef, PyType, PyTypeRef}, @@ -534,7 +535,30 @@ pub(crate) fn richcompare_wrapper( } fn iter_wrapper(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { - vm.call_special_method(&zelf, identifier!(vm, __iter__), ()) + fn method_is_none(method: &PyMethod, vm: &VirtualMachine) -> bool { + match method { + PyMethod::Attribute(meth) => vm.is_none(meth), + _ => false, + } + } + + // + // look-up '__iter__' method + // + let method_ident = identifier!(vm, __iter__); + let method = vm + .get_special_method(&zelf, method_ident)? + .ok_or_else(|| vm.new_attribute_error(method_ident.as_str().to_owned()))?; + + // + // setting '__iter__' method to 'None' value is used + // to mark non-iterable objects, raise TypeError + // + if method_is_none(&method, vm) { + return Err(vm.new_type_error(format!("'{}' object is not iterable", zelf.class().name()))); + } + + method.invoke((), vm) } fn bool_wrapper(num: PyNumber<'_>, vm: &VirtualMachine) -> PyResult {