diff --git a/graalpython/com.oracle.graal.python.test.integration/pom.xml b/graalpython/com.oracle.graal.python.test.integration/pom.xml
index 14dd9c7f2c..20803f3050 100644
--- a/graalpython/com.oracle.graal.python.test.integration/pom.xml
+++ b/graalpython/com.oracle.graal.python.test.integration/pom.xml
@@ -64,7 +64,7 @@ Additionally, one can change the polyglot artifacts version with
17
17
UTF-8
- 25.1.3
+ 25.2.3
diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/PythonTests.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/PythonTests.java
index e5874b01fd..d1fd62e8dd 100644
--- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/PythonTests.java
+++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/PythonTests.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) 2013, Regents of the University of California
*
* All rights reserved.
@@ -76,17 +76,21 @@ public static Context enterContext(Map options, String[] args) {
return context;
}
- private static void closeContext(Context ctxt) {
+ private static void closeContext(Context ctxt, boolean cancelIfExecuting) {
try {
ctxt.leave();
} catch (RuntimeException e) {
}
- ctxt.close();
+ ctxt.close(cancelIfExecuting);
}
public static void closeContext() {
+ closeContext(false);
+ }
+
+ public static void closeContext(boolean cancelIfExecuting) {
if (context != null) {
- closeContext(context);
+ closeContext(context, cancelIfExecuting);
context = null;
}
}
diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/module/ThreadPoolTests.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/module/ThreadPoolTests.java
index b98ff80a05..319990c99e 100644
--- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/module/ThreadPoolTests.java
+++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/module/ThreadPoolTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023, 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
@@ -68,11 +68,12 @@ public void threadPool() {
"pool = ThreadPool(2)\n" +
"res = list(pool.imap(fun, items))\n" +
"pool.close()\n" +
+ "pool.join()\n" +
"\n" +
"print(res)\n";
final ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
final PrintStream printStream = new PrintStream(byteArray);
- PythonTests.runScript(new HashMap<>(), new String[0], source, printStream, System.err, () -> PythonTests.closeContext());
+ PythonTests.runScript(new HashMap<>(), new String[0], source, printStream, System.err, () -> PythonTests.closeContext(true));
String result = byteArray.toString().replaceAll("\r\n", "\n");
assertEquals("[True, True, True, True, True, True, True, True, True, True]\n", result);
}
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_bytes.py b/graalpython/com.oracle.graal.python.test/src/tests/test_bytes.py
index a379b1c93e..309ba2a0a8 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/test_bytes.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/test_bytes.py
@@ -37,6 +37,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
+import base64
import sys
import unittest
@@ -629,6 +630,22 @@ def test_strip_bytes():
assert b'abc'.lstrip(b'ac') == b'bc'
assert b'abc'.rstrip(b'ac') == b'ab'
+
+def test_strip_default_whitespace():
+ whitespace = b'\x09\x0a\x0b\x0c\x0d '
+ non_whitespace = b'\x1c\x1d\x1e\x1f'
+ for type2test in (bytes, bytearray):
+ value = type2test(whitespace + non_whitespace + whitespace)
+ assert value.strip() == non_whitespace
+ assert value.lstrip() == non_whitespace + whitespace
+ assert value.rstrip() == whitespace + non_whitespace
+
+
+def test_strip_base32_result_with_control_bytes():
+ value = base64.b32decode(b'INFAO2ZW7APB2===')
+ assert value == b'CJ\x07k6\xf8\x1e\x1d'
+ assert value.strip() == value
+
class BaseTestSplit:
def test_string_error(self):
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py b/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py
index 0bd4a412a8..4b3070171d 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py
@@ -272,16 +272,17 @@ def test_open_bytes_path(self):
def test_failed_read_write_errno(self):
read_fd, write_fd = os.pipe()
- os.close(read_fd)
- os.close(write_fd)
-
- with self.assertRaises(OSError) as read_error:
- os.read(read_fd, 1)
- self.assertEqual(errno.EBADF, read_error.exception.errno)
+ try:
+ with self.assertRaises(OSError) as read_error:
+ os.read(write_fd, 1)
+ self.assertEqual(errno.EBADF, read_error.exception.errno)
- with self.assertRaises(OSError) as write_error:
- os.write(write_fd, b'x')
- self.assertEqual(errno.EBADF, write_error.exception.errno)
+ with self.assertRaises(OSError) as write_error:
+ os.write(read_fd, b'x')
+ self.assertEqual(errno.EBADF, write_error.exception.errno)
+ finally:
+ os.close(read_fd)
+ os.close(write_fd)
def test_fd_converter(self):
class MyInt(int):
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_re.py b/graalpython/com.oracle.graal.python.test/src/tests/test_re.py
index 29d2a98033..807e3a493f 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/test_re.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/test_re.py
@@ -317,6 +317,15 @@ def test_re_subn(self):
self.assertEqual(re.subn("b*", "x", "xyz", 2), ('xxxyz', 2))
self.assertEqual(re.subn("b*", "x", "xyz", count=2), ('xxxyz', 2))
+ def test_terminal_empty_match_after_consuming_match(self):
+ cases = [
+ (r"(?:(?P\*)){0,2}", "*"),
+ (r"(?:(?:\s){0,2}){0,2}", " "),
+ ]
+ for pattern, string in cases:
+ self.assertEqual(re.sub(pattern, "X", string), "XX")
+ self.assertEqual(re.subn(pattern, "X", string), ("XX", 2))
+
def test_re_split(self):
for string in ":a:b::c", S(":a:b::c"):
self.assertTypedEqual(re.split(":", string),
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_repr.py b/graalpython/com.oracle.graal.python.test/src/tests/test_repr.py
index a99aa531a3..2cbc6587e1 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/test_repr.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/test_repr.py
@@ -86,6 +86,34 @@ def __repr__(self):
assert str(e) == "__repr__ returned non-string (type int)"
+def test_numeric_str_uses_overridden_repr():
+ class IntSubclass(int):
+ def __repr__(self):
+ return "IntSubclass"
+
+ class FloatSubclass(float):
+ def __repr__(self):
+ return "FloatSubclass"
+
+ class ComplexSubclass(complex):
+ def __repr__(self):
+ return "ComplexSubclass"
+
+ for numeric_type in (int, float, bool, complex):
+ assert numeric_type.__str__ is object.__str__
+
+ for value, expected in (
+ (IntSubclass(), "IntSubclass"),
+ (FloatSubclass(), "FloatSubclass"),
+ (ComplexSubclass(), "ComplexSubclass"),
+ ):
+ assert repr(value) == expected
+ assert str(value) == expected
+
+ assert str(True) == "True"
+ assert repr(True) == "True"
+
+
def test_repr_deep_userlist_raises_recursion_error():
a = UserList([])
for _ in range(REPR_RECURSION_LIMIT + 10):
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/PatternBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/PatternBuiltins.java
index 2c11b8288f..0c0836a211 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/PatternBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/PatternBuiltins.java
@@ -1191,7 +1191,7 @@ private static Object doReplace(Object compiledRegex, Object compiledRegexMustAd
int n = 0;
int pos = 0;
boolean mustAdvance = false;
- while ((count == 0 || n < count) && pos < stringLength) {
+ while ((count == 0 || n < count) && pos <= stringLength) {
final Object searchResult;
if (mustAdvance) {
searchResult = invokeExecMethodNodeMustAdvance.execute(inliningTarget, compiledRegexMustAdvance, input, pos);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bool/BoolBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bool/BoolBuiltins.java
index f0f7a60502..2b180c11c8 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bool/BoolBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bool/BoolBuiltins.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.
@@ -76,7 +76,7 @@ public static boolean bool(VirtualFrame frame, @SuppressWarnings("unused") Objec
}
}
- @Slot(value = SlotKind.tp_str, isComplex = true)
+ @Slot(value = SlotKind.tp_repr, isComplex = true)
@TypeSystemReference(PythonIntegerTypes.class)
@GenerateNodeFactory
abstract static class StrNode extends PythonUnaryBuiltinNode {
@@ -96,11 +96,6 @@ public static TruffleString doPInt(PInt self) {
}
}
- @Slot(value = SlotKind.tp_repr, isComplex = true)
- @GenerateNodeFactory
- abstract static class RepNode extends StrNode {
- }
-
@Slot(value = SlotKind.nb_and, isComplex = true)
@GenerateNodeFactory
abstract static class AndNode extends BinaryOpBuiltinNode {
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 f401e4aacb..16bd5e6bd4 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
@@ -1922,7 +1922,7 @@ protected int findIndex(byte[] bs) {
@TruffleBoundary
private static boolean isWhitespace(byte b) {
- return Character.isWhitespace(b);
+ return BytesUtils.isSpace(b);
}
protected int findIndex(byte[] bs, byte[] stripBs, int stripBsLen) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java
index 48e340f7ad..f0fc8e72c2 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java
@@ -325,7 +325,7 @@ protected static boolean isPrimitiveFloat(Node inliningTarget, Object cls, Built
}
}
- @Slot(value = SlotKind.tp_str, isComplex = true)
+ @Slot(value = SlotKind.tp_repr, isComplex = true)
@GenerateNodeFactory
public abstract static class StrNode extends AbstractNumericUnaryBuiltin {
public static final Spec spec = new Spec(' ', '>', Spec.NONE, false, Spec.UNSPECIFIED, Spec.NONE, 0, 'r');
@@ -343,11 +343,6 @@ public static TruffleString doFormat(double d, FloatFormatter f) {
}
}
- @Slot(value = SlotKind.tp_repr, isComplex = true)
- @GenerateNodeFactory
- abstract static class ReprNode extends StrNode {
- }
-
@Builtin(name = J___FORMAT__, minNumOfPositionalArgs = 2, parameterNames = {"$self", "format_spec"})
@ArgumentClinic(name = "format_spec", conversion = ClinicConversion.TString)
@GenerateNodeFactory
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 477dfe3e70..a1c1e600da 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
@@ -2534,7 +2534,7 @@ static boolean toBoolean(PInt self) {
}
}
- @Slot(value = SlotKind.tp_str, isComplex = true)
+ @Slot(value = SlotKind.tp_repr, isComplex = true)
@GenerateNodeFactory
@TypeSystemReference(PythonIntegerTypes.class)
abstract static class StrNode extends PythonUnaryBuiltinNode {
@@ -2592,11 +2592,6 @@ private static int positiveBitLength(PInt self) {
}
}
- @Slot(value = SlotKind.tp_repr, isComplex = true)
- @GenerateNodeFactory
- abstract static class ReprNode extends StrNode {
- }
-
@Builtin(name = J___FORMAT__, minNumOfPositionalArgs = 2, parameterNames = {"$self", "format_spec"})
@ArgumentClinic(name = "format_spec", conversion = ClinicConversion.TString)
@GenerateNodeFactory
diff --git a/mx.graalpython/mx_graalpython_python_benchmarks.py b/mx.graalpython/mx_graalpython_python_benchmarks.py
index 330e5b4725..f8369e981d 100644
--- a/mx.graalpython/mx_graalpython_python_benchmarks.py
+++ b/mx.graalpython/mx_graalpython_python_benchmarks.py
@@ -134,15 +134,26 @@
def add_cpython_build_env(env=None):
if python3_home := os.environ.get("PYTHON3_HOME"):
- include_dir = join(python3_home, "Include")
- if os.path.exists(join(include_dir, "Python.h")):
+ checkout_include = join(python3_home, "Include")
+ installed_includes = sorted(glob.glob(join(python3_home, os.pardir, "include", "python*")))
+ include_dir = next(
+ (path for path in [checkout_include, *installed_includes] if os.path.exists(join(path, "Python.h"))),
+ None,
+ )
+ if include_dir:
env = env.copy() if env is not None else os.environ.copy()
- python_includes = os.pathsep.join([include_dir, python3_home])
+ include_dirs = [include_dir]
+ if include_dir == checkout_include:
+ include_dirs.append(python3_home)
+ python_includes = os.pathsep.join(include_dirs)
include_flags = " ".join(f"-I{path}" for path in python_includes.split(os.pathsep))
env["CPATH"] = python_includes + (os.pathsep + env["CPATH"] if env.get("CPATH") else "")
for key in ["CFLAGS", "CPPFLAGS", "CXXFLAGS"]:
env[key] = include_flags + (" " + env[key] if env.get(key) else "")
- env["LIBRARY_PATH"] = python3_home + (os.pathsep + env["LIBRARY_PATH"] if env.get("LIBRARY_PATH") else "")
+ lib_dir = join(python3_home, os.pardir, "lib")
+ if not os.path.isdir(lib_dir):
+ lib_dir = python3_home
+ env["LIBRARY_PATH"] = lib_dir + (os.pathsep + env["LIBRARY_PATH"] if env.get("LIBRARY_PATH") else "")
return env