Skip to content

Commit 7430068

Browse files
committed
Use length hint when creating tuples
Fixes #756
1 parent 90e4e52 commit 7430068

3 files changed

Lines changed: 40 additions & 4 deletions

File tree

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2024, Oracle and/or its affiliates.
1+
# Copyright (c) 2018, 2026, Oracle and/or its affiliates.
22
# Copyright (C) 1996-2017 Python Software Foundation
33
#
44
# Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -283,6 +283,38 @@ def maketuple(t):
283283
self.assertTrue(tuple(b) is b)
284284
self.assertFalse(tuple(a) is a)
285285

286+
def test_constructor_validates_length_hint_before_iteration(self):
287+
class BadLengthHint:
288+
def __getitem__(self, index):
289+
raise AssertionError("__getitem__ should not be called")
290+
291+
def __length_hint__(self):
292+
return None
293+
294+
with self.assertRaisesRegex(TypeError, "__length_hint__ must be an integer"):
295+
tuple(BadLengthHint())
296+
297+
def test_constructor_validates_len_before_iteration(self):
298+
class BadLen:
299+
def __getitem__(self, index):
300+
raise AssertionError("__getitem__ should not be called")
301+
302+
def __len__(self):
303+
return -1
304+
305+
with self.assertRaisesRegex(ValueError, "__len__\\(\\) should return >= 0"):
306+
tuple(BadLen())
307+
308+
def test_constructor_length_hint_too_small(self):
309+
class SmallLengthHint:
310+
def __iter__(self):
311+
return iter((1, 2, 3))
312+
313+
def __length_hint__(self):
314+
return 1
315+
316+
self.assertEqual(tuple(SmallLengthHint()), (1, 2, 3))
317+
286318

287319
class TupleCompareTest(CompareTest):
288320

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/iterator/IteratorNodes.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -111,6 +111,7 @@ public abstract class IteratorNodes {
111111
*/
112112
@GenerateInline
113113
@GenerateCached(false)
114+
@GenerateUncached
114115
@ImportStatic({PGuards.class, SpecialMethodNames.class})
115116
public abstract static class GetLength extends PNodeWithContext {
116117

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/TupleNodes.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -49,6 +49,7 @@
4949
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
5050
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
5151
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.CreateStorageFromIteratorNode;
52+
import com.oracle.graal.python.builtins.objects.iterator.IteratorNodes;
5253
import com.oracle.graal.python.builtins.objects.list.PList;
5354
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
5455
import com.oracle.graal.python.lib.PyObjectGetIter;
@@ -101,10 +102,12 @@ static PTuple list(PList iterable,
101102
static PTuple generic(VirtualFrame frame, Object iterable,
102103
@Bind Node inliningTarget,
103104
@Bind PythonLanguage language,
105+
@Cached IteratorNodes.GetLength lenNode,
104106
@Cached CreateStorageFromIteratorNode storageNode,
105107
@Cached PyObjectGetIter getIter) {
108+
int len = lenNode.execute(frame, inliningTarget, iterable);
106109
Object iterObj = getIter.execute(frame, inliningTarget, iterable);
107-
return PFactory.createTuple(language, storageNode.execute(frame, iterObj));
110+
return PFactory.createTuple(language, storageNode.execute(frame, iterObj, len));
108111
}
109112

110113
@NeverDefault

0 commit comments

Comments
 (0)