Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion graalpython/com.oracle.graal.python.test/src/tests/test_tuple.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2018, 2024, Oracle and/or its affiliates.
# Copyright (c) 2018, 2026, Oracle and/or its affiliates.
# Copyright (C) 1996-2017 Python Software Foundation
#
# Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
Expand Down Expand Up @@ -283,6 +283,38 @@ def maketuple(t):
self.assertTrue(tuple(b) is b)
self.assertFalse(tuple(a) is a)

def test_constructor_validates_length_hint_before_iteration(self):
class BadLengthHint:
def __getitem__(self, index):
raise AssertionError("__getitem__ should not be called")

def __length_hint__(self):
return None

with self.assertRaisesRegex(TypeError, "__length_hint__ must be an integer"):
tuple(BadLengthHint())

def test_constructor_validates_len_before_iteration(self):
class BadLen:
def __getitem__(self, index):
raise AssertionError("__getitem__ should not be called")

def __len__(self):
return -1

with self.assertRaisesRegex(ValueError, "__len__\\(\\) should return >= 0"):
tuple(BadLen())

def test_constructor_length_hint_too_small(self):
class SmallLengthHint:
def __iter__(self):
return iter((1, 2, 3))

def __length_hint__(self):
return 1

self.assertEqual(tuple(SmallLengthHint()), (1, 2, 3))


class TupleCompareTest(CompareTest):

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -111,6 +111,7 @@ public abstract class IteratorNodes {
*/
@GenerateInline
@GenerateCached(false)
@GenerateUncached
@ImportStatic({PGuards.class, SpecialMethodNames.class})
public abstract static class GetLength extends PNodeWithContext {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -49,6 +49,7 @@
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.CreateStorageFromIteratorNode;
import com.oracle.graal.python.builtins.objects.iterator.IteratorNodes;
import com.oracle.graal.python.builtins.objects.list.PList;
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
import com.oracle.graal.python.lib.PyObjectGetIter;
Expand Down Expand Up @@ -101,10 +102,12 @@ static PTuple list(PList iterable,
static PTuple generic(VirtualFrame frame, Object iterable,
@Bind Node inliningTarget,
@Bind PythonLanguage language,
@Cached IteratorNodes.GetLength lenNode,
@Cached CreateStorageFromIteratorNode storageNode,
@Cached PyObjectGetIter getIter) {
int len = lenNode.execute(frame, inliningTarget, iterable);
Object iterObj = getIter.execute(frame, inliningTarget, iterable);
return PFactory.createTuple(language, storageNode.execute(frame, iterObj));
return PFactory.createTuple(language, storageNode.execute(frame, iterObj, len));
}

@NeverDefault
Expand Down
Loading