Skip to content

Commit 5b8db00

Browse files
committed
fix GetIteratorNode when the __iter__ method returns None (this fixes GR-10392)
- add new test to test_iter to check for cases when the iter is exhausted in a try/except block
1 parent 9918219 commit 5b8db00

File tree

3 files changed

+34
-9
lines changed

3 files changed

+34
-9
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_iterator.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,16 @@ def test_iter():
100100

101101
def test_zip_no_args():
102102
assert list(zip(*[])) == []
103+
104+
105+
def test_iter_try_except():
106+
it = iter(range(3))
107+
exit_via_break = False
108+
while 1:
109+
try:
110+
next(it)
111+
except StopIteration:
112+
exit_via_break = True
113+
break
114+
115+
assert exit_via_break

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/control/GetIteratorNode.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
*/
2626
package com.oracle.graal.python.nodes.control;
2727

28+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GETITEM__;
29+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__ITER__;
30+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__NEXT__;
2831
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
2932

3033
import com.oracle.graal.python.builtins.objects.PNone;
@@ -41,7 +44,6 @@
4144
import com.oracle.graal.python.builtins.objects.range.PRange;
4245
import com.oracle.graal.python.builtins.objects.type.PythonClass;
4346
import com.oracle.graal.python.nodes.PNode;
44-
import com.oracle.graal.python.nodes.SpecialMethodNames;
4547
import com.oracle.graal.python.nodes.argument.CreateArgumentsNode;
4648
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
4749
import com.oracle.graal.python.nodes.call.CallDispatchNode;
@@ -162,16 +164,16 @@ public Object doGeneric(Object value,
162164
@Cached("create()") CreateArgumentsNode createArgs,
163165
@Cached("create()") IsIteratorObjectNode isIteratorObjectNode) {
164166
PythonClass clazz = getClass(value);
165-
Object attrObj = getattributeProfile.profile(lookupAttrMroNode.execute(clazz, SpecialMethodNames.__ITER__));
166-
if (attrObj != PNone.NO_VALUE) {
167-
Object iterObj = dispatchGetattribute.executeCall(attrObj, createArgs.execute(new Object[]{value}), PKeyword.EMPTY_KEYWORDS);
167+
Object attrObj = getattributeProfile.profile(lookupAttrMroNode.execute(clazz, __ITER__));
168+
if (attrObj != PNone.NO_VALUE && attrObj != PNone.NONE) {
169+
Object iterObj = dispatchGetattribute.executeCall(attrObj, createArgs.execute(value), PKeyword.EMPTY_KEYWORDS);
168170
if (isIteratorObjectNode.execute(iterObj)) {
169171
return iterObj;
170172
} else {
171173
throw nonIterator(iterObj);
172174
}
173175
} else {
174-
Object getItemAttrObj = lookupGetitemAttrMroNode.execute(clazz, SpecialMethodNames.__GETITEM__);
176+
Object getItemAttrObj = lookupGetitemAttrMroNode.execute(clazz, __GETITEM__);
175177
if (getItemAttrObj != PNone.NO_VALUE) {
176178
return factory().createSequenceIterator(value);
177179
}
@@ -206,7 +208,7 @@ boolean doPIterator(@SuppressWarnings("unused") PBuiltinIterator it) {
206208
boolean doPIterator(Object it,
207209
@Cached("create()") GetClassNode getClassNode,
208210
@Cached("create()") LookupAttributeInMRONode lookupAttributeNode) {
209-
return lookupAttributeNode.execute(getClassNode.execute(it), SpecialMethodNames.__NEXT__) != PNone.NO_VALUE;
211+
return lookupAttributeNode.execute(getClassNode.execute(it), __NEXT__) != PNone.NO_VALUE;
210212
}
211213

212214
public static IsIteratorObjectNode create() {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/datamodel/IsIterableNode.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
*/
3939
package com.oracle.graal.python.nodes.datamodel;
4040

41+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GETITEM__;
4142
import static com.oracle.graal.python.nodes.SpecialMethodNames.__ITER__;
4243
import static com.oracle.graal.python.nodes.SpecialMethodNames.__NEXT__;
4344

@@ -57,8 +58,11 @@
5758
public abstract class IsIterableNode extends PDataModelEmulationNode {
5859
@Child private HasInheritedAttributeNode hasNextNode = HasInheritedAttributeNode.create(__NEXT__);
5960
@Child private LookupInheritedAttributeNode getIterNode = LookupInheritedAttributeNode.create();
61+
@Child private LookupInheritedAttributeNode getGetItemNode = LookupInheritedAttributeNode.create();
62+
@Child private IsCallableNode isCallableNode = IsCallableNode.create();
6063

6164
private final ConditionProfile profileIter = ConditionProfile.createBinaryProfile();
65+
private final ConditionProfile profileGetItem = ConditionProfile.createBinaryProfile();
6266
private final ConditionProfile profileNext = ConditionProfile.createBinaryProfile();
6367

6468
@Specialization
@@ -104,11 +108,17 @@ public boolean isIterable(@SuppressWarnings("unused") PZip zip) {
104108
@Specialization
105109
public boolean isIterable(Object object) {
106110
Object iterMethod = getIterNode.execute(object, __ITER__);
107-
if (profileIter.profile(iterMethod != PNone.NO_VALUE)) {
108-
return iterMethod != PNone.NONE;
111+
if (profileIter.profile(iterMethod != PNone.NO_VALUE && iterMethod != PNone.NONE)) {
112+
return true;
109113
} else {
110-
return profileNext.profile(hasNextNode.execute(object));
114+
Object getItemMethod = getGetItemNode.execute(object, __GETITEM__);
115+
if (profileGetItem.profile(getItemMethod != PNone.NO_VALUE)) {
116+
return true;
117+
} else if (isCallableNode.execute(object)) {
118+
return profileNext.profile(hasNextNode.execute(object));
119+
}
111120
}
121+
return false;
112122
}
113123

114124
public static IsIterableNode create() {

0 commit comments

Comments
 (0)