From 5b58885781f4eeb5aa769b7aa6d83fbd6ab246b0 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 3 Mar 2026 13:49:07 +0100 Subject: [PATCH 1/3] Fix different behavior of string split. --- .../com.oracle.graal.python.test/src/tests/test_string.py | 8 +++++++- .../graal/python/builtins/objects/str/StringBuiltins.java | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_string.py b/graalpython/com.oracle.graal.python.test/src/tests/test_string.py index aea27924f9..afe7b5aa6c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_string.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_string.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2025, 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 @@ -1095,6 +1095,12 @@ def test_splitlines(): assert len(str.splitlines("a\nb")) == 2 +def test_split_negative_maxsplit_matches_unlimited(): + s = "0x1.e800000000000p+5" + assert s.split(s, maxsplit=-84) == ["", ""] + assert s.split(s, maxsplit=-1) == ["", ""] + + def test_literals(): s = "hello\[world\]" assert len(s) == 14 diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java index 6b7afc2514..a82bbdc8fc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java @@ -1277,7 +1277,7 @@ static PList doStringSep(TruffleString self, TruffleString sep, int maxsplit, if (sep.isEmpty()) { throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.EMPTY_SEPARATOR); } - int splits = maxsplit == -1 ? Integer.MAX_VALUE : maxsplit; + int splits = maxsplit < 0 ? Integer.MAX_VALUE : maxsplit; PList list = PFactory.createList(PythonLanguage.get(inliningTarget)); int lastEnd = 0; From 9fb25e68d34e74096049b00c4eb42447b8d381a2 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 3 Mar 2026 14:17:07 +0100 Subject: [PATCH 2/3] [GR-72841] Handle CannotCastException in splitlines --- .../src/tests/test_string.py | 6 +++- .../objects/bytes/BytesCommonBuiltins.java | 30 +++++++------------ .../builtins/objects/str/StringBuiltins.java | 21 ++++++------- 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_string.py b/graalpython/com.oracle.graal.python.test/src/tests/test_string.py index afe7b5aa6c..e8f02c9ada 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_string.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_string.py @@ -168,6 +168,10 @@ def test_strip(): assert u' test '.strip() == u'test' +def test_splitlines_keepends_type_error(): + assert "foo\n".splitlines("bla") == ["foo\n"] + + def assertEqual(value, expected): assert value == expected, ("'%s' was expected to be equal to '%s'" % (value, expected)) @@ -1239,4 +1243,4 @@ def test_fstring(): assert type(f"hello {FunkyFormat('world')}!") == str assert f"hello {FunkyFormat('world')}!" == "hello world!" assert f"hello {FunkyStr('world')}!" == "hello world!" - assertRaises(TypeError, lambda: f"hello {FunkyFormat(33)}!") \ No newline at end of file + assertRaises(TypeError, lambda: f"hello {FunkyFormat(33)}!") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java index be1e1476e3..f4f6cb56cf 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. * Copyright (c) 2014, Regents of the University of California * * All rights reserved. @@ -96,7 +96,6 @@ import com.oracle.graal.python.lib.PyNumberIndexNode; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; -import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.SpecialAttributeNames; import com.oracle.graal.python.nodes.SpecialMethodNames; @@ -112,7 +111,6 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentCastNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; -import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext; import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; @@ -1804,30 +1802,22 @@ protected List splitDelimiter(byte[] bytes, int len, byte[] sep, int max // bytes.splitlines([keepends]) // bytearray.splitlines([keepends]) @Builtin(name = "splitlines", minNumOfPositionalArgs = 1, parameterNames = {"self", "keepends"}) + @ArgumentClinic(name = "keepends", conversion = ArgumentClinic.ClinicConversion.Boolean, defaultValue = "false") @GenerateNodeFactory - public abstract static class SplitLinesNode extends PythonBinaryBuiltinNode { + public abstract static class SplitLinesNode extends PythonBinaryClinicBuiltinNode { + + @Override + protected ArgumentClinicProvider getArgumentClinic() { + return BytesCommonBuiltinsClinicProviders.SplitLinesNodeClinicProviderGen.INSTANCE; + } + @Specialization - static PList doSplitlines(Object self, Object keependsObj, + static PList doSplitlines(Object self, boolean keepends, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached InlinedBranchProfile isPNoneProfile, - @Cached InlinedBranchProfile isBooleanProfile, - @Cached InlinedConditionProfile keependsProfile, - @Cached CastToJavaIntExactNode cast, @Cached BytesNodes.ToBytesNode toBytesNode, @Cached ListNodes.AppendNode appendNode, @Cached BytesNodes.CreateBytesNode create) { - boolean keepends; - if (keependsObj instanceof Boolean b) { - isBooleanProfile.enter(inliningTarget); - keepends = b; - } else if (PGuards.isPNone(keependsObj)) { - isPNoneProfile.enter(inliningTarget); - keepends = false; - } else { - keepends = cast.execute(inliningTarget, keependsObj) != 0; - } - keepends = keependsProfile.profile(inliningTarget, keepends); byte[] bytes = toBytesNode.execute(null, self); PList list = PFactory.createList(language); int sliceStart = 0; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java index a82bbdc8fc..18fa591fcb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java @@ -108,6 +108,7 @@ import com.oracle.graal.python.builtins.objects.slice.SliceNodes.ComputeIndices; import com.oracle.graal.python.builtins.objects.str.StringBuiltinsClinicProviders.FormatNodeClinicProviderGen; import com.oracle.graal.python.builtins.objects.str.StringBuiltinsClinicProviders.SplitNodeClinicProviderGen; +import com.oracle.graal.python.builtins.objects.str.StringBuiltinsClinicProviders.SplitLinesNodeClinicProviderGen; import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToJavaStringCheckedNode; import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked0Node; import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; @@ -157,7 +158,6 @@ import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinClassExactProfile; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.nodes.util.CannotCastException; -import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext; import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; @@ -1460,14 +1460,13 @@ static PList doStringMaxsplit(VirtualFrame frame, TruffleString s, @SuppressWarn // str.splitlines([keepends]) @Builtin(name = "splitlines", minNumOfPositionalArgs = 1, parameterNames = {"self", "keepends"}) + @ArgumentClinic(name = "keepends", conversion = ClinicConversion.Boolean, defaultValue = "false") @GenerateNodeFactory - public abstract static class SplitLinesNode extends PythonBinaryBuiltinNode { + public abstract static class SplitLinesNode extends PythonBinaryClinicBuiltinNode { - @Specialization - static PList doString(TruffleString self, @SuppressWarnings("unused") PNone keepends, - @Bind Node inliningTarget, - @Cached @Shared SplitLinesInnerNode innerNode) { - return innerNode.execute(inliningTarget, self, false); + @Override + protected ArgumentClinicProvider getArgumentClinic() { + return SplitLinesNodeClinicProviderGen.INSTANCE; } @Specialization @@ -1477,15 +1476,13 @@ static PList doStringKeepends(TruffleString selfTs, boolean keepends, return innerNode.execute(inliningTarget, selfTs, keepends); } - @Specialization(replaces = {"doString", "doStringKeepends"}) - static PList doGeneric(Object self, Object keepends, + @Specialization(replaces = "doStringKeepends") + static PList doGeneric(Object self, boolean keepends, @Bind Node inliningTarget, @Cached CastToTruffleStringChecked2Node castSelfNode, - @Cached CastToJavaIntExactNode castToJavaIntNode, @Cached @Exclusive SplitLinesInnerNode innerNode) { TruffleString selfStr = castSelfNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "splitlines", self); - boolean bKeepends = !PGuards.isPNone(keepends) && castToJavaIntNode.execute(inliningTarget, keepends) != 0; - return innerNode.execute(inliningTarget, selfStr, bKeepends); + return innerNode.execute(inliningTarget, selfStr, keepends); } @GenerateCached(false) From 312838a5e8b1d9e497a4632d24ebe4ccbfef29de Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Tue, 3 Mar 2026 14:40:17 +0100 Subject: [PATCH 3/3] [GR-71716] Fix return type of math.ceil for int subclasses --- .../com.oracle.graal.python.test/src/tests/test_math.py | 1 + .../graal/python/builtins/objects/ints/IntBuiltins.java | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_math.py b/graalpython/com.oracle.graal.python.test/src/tests/test_math.py index c07a896a4b..52af5bf203 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_math.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_math.py @@ -419,6 +419,7 @@ def __ceil__(self): return 'hello' self.assertEqual(math.ceil(I(22)), 22) + self.assertEqual(type(math.ceil(I(22))), int) self.assertEqual(math.ceil(I2(256)), 11) self.assertEqual(math.ceil(I(156)), 156) self.assertEqual(math.ceil(I2(777)), 11) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java index 4b07364ac0..5a20717771 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java @@ -1541,8 +1541,9 @@ static long ceil(long arg) { } @Specialization - static PInt ceil(PInt arg) { - return arg; + static PInt ceil(PInt arg, + @Bind PythonLanguage language) { + return PFactory.createInt(language, arg.getValue()); } }