diff --git a/ci.jsonnet b/ci.jsonnet
index 48bf915fd3..7d5f235fe3 100644
--- a/ci.jsonnet
+++ b/ci.jsonnet
@@ -1 +1 @@
-{ "overlay": "840dea7f9575e2e96e2143685751932513fd0c78" }
+{ "overlay": "16d0e566573ebdcf380bf6f309c02724baf4172b" }
diff --git a/graalpython/com.oracle.graal.python.cext/include/cpython/tupleobject.h b/graalpython/com.oracle.graal.python.cext/include/cpython/tupleobject.h
index ef3d3c3790..a5bb3fba3b 100644
--- a/graalpython/com.oracle.graal.python.cext/include/cpython/tupleobject.h
+++ b/graalpython/com.oracle.graal.python.cext/include/cpython/tupleobject.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2020, 2024, Oracle and/or its affiliates.
+/* Copyright (c) 2020, 2025, Oracle and/or its affiliates.
* Copyright (C) 1996-2020 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -39,14 +39,14 @@ PyAPI_FUNC(PyObject *) _PyTuple_GET_ITEM(PyObject *, Py_ssize_t);
// GraalPy-specific
PyAPI_FUNC(PyObject **) PyTruffleTuple_GetItems(PyObject *op);
-/* Function *only* to be used to fill in brand new tuples */
-static inline void
-PyTuple_SET_ITEM(PyObject *op, Py_ssize_t index, PyObject *value) {
+// GraalPy change: Export PyTuple_SET_ITEM as regular API function to use in PyO3 and others
+PyAPI_FUNC(void) PyTuple_SET_ITEM(PyObject*, Py_ssize_t, PyObject*);
+
+/* Inline function to be used in the PyTuple_SET_ITEM macro. */
+static inline void graalpy_tuple_set_item(PyObject *op, Py_ssize_t index, PyObject *value) {
PyTruffleTuple_GetItems(op)[index] = value;
}
-#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
#define PyTuple_SET_ITEM(op, index, value) \
- PyTuple_SET_ITEM(_PyObject_CAST(op), index, _PyObject_CAST(value))
-#endif
+ graalpy_tuple_set_item(_PyObject_CAST(op), (index), _PyObject_CAST(value))
PyAPI_FUNC(void) _PyTuple_DebugMallocStats(FILE *out);
diff --git a/graalpython/com.oracle.graal.python.cext/include/object.h b/graalpython/com.oracle.graal.python.cext/include/object.h
index 7d88c22cb1..a427c70040 100644
--- a/graalpython/com.oracle.graal.python.cext/include/object.h
+++ b/graalpython/com.oracle.graal.python.cext/include/object.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018, 2024, Oracle and/or its affiliates.
+/* Copyright (c) 2018, 2025, Oracle and/or its affiliates.
* Copyright (C) 1996-2020 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -124,7 +124,9 @@ typedef struct {
// Test if the 'x' object is the 'y' object, the same as "x is y" in Python.
PyAPI_FUNC(int) Py_Is(PyObject *x, PyObject *y);
+#if 0 // GraalPy change
#define Py_Is(x, y) ((x) == (y))
+#endif // GraalPy change
PyAPI_FUNC(Py_ssize_t) PyTruffle_REFCNT(PyObject *ob);
diff --git a/graalpython/com.oracle.graal.python.cext/src/object.c b/graalpython/com.oracle.graal.python.cext/src/object.c
index 9ad7908e8a..d467aac946 100644
--- a/graalpython/com.oracle.graal.python.cext/src/object.c
+++ b/graalpython/com.oracle.graal.python.cext/src/object.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018, 2024, Oracle and/or its affiliates.
+/* Copyright (c) 2018, 2025, Oracle and/or its affiliates.
* Copyright (C) 1996-2022 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -2507,7 +2507,12 @@ Py_XNewRef(PyObject *obj)
// for the stable ABI.
int Py_Is(PyObject *x, PyObject *y)
{
+#if 0 // GraalPy change
return (x == y);
+#else
+ return (x == y) ||
+ (points_to_py_handle_space(x) && points_to_py_handle_space(y) && GraalPyTruffle_Is(x, y));
+#endif
}
int Py_IsNone(PyObject *x)
diff --git a/graalpython/com.oracle.graal.python.cext/src/tupleobject.c b/graalpython/com.oracle.graal.python.cext/src/tupleobject.c
index 007bab220d..4e6f9c2a50 100644
--- a/graalpython/com.oracle.graal.python.cext/src/tupleobject.c
+++ b/graalpython/com.oracle.graal.python.cext/src/tupleobject.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018, 2024, Oracle and/or its affiliates.
+/* Copyright (c) 2018, 2025, Oracle and/or its affiliates.
* Copyright (C) 1996-2022 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -1424,3 +1424,9 @@ _PyTuple_GET_ITEM(PyObject* a, Py_ssize_t b) {
}
return NULL; // an exception has happend during transtion
}
+
+#undef PyTuple_SET_ITEM
+// Export PyTuple_SET_ITEM as regular API function to use in PyO3 and others
+void PyTuple_SET_ITEM(PyObject* op, Py_ssize_t index, PyObject* value) {
+ graalpy_tuple_set_item(op, index, value);
+}
diff --git a/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c b/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c
index 42699b743a..852906637e 100644
--- a/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c
+++ b/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018, 2024, Oracle and/or its affiliates.
+/* Copyright (c) 2018, 2025, Oracle and/or its affiliates.
* Copyright (C) 1996-2020 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -6220,16 +6220,17 @@ _PyUnicode_DecodeRawUnicodeEscapeStateful(const char *s,
Py_XDECREF(exc);
return NULL;
}
-
+#endif // GraalPy change
PyObject *
PyUnicode_DecodeRawUnicodeEscape(const char *s,
Py_ssize_t size,
const char *errors)
{
- return _PyUnicode_DecodeRawUnicodeEscapeStateful(s, size, errors, NULL);
+ return PyUnicode_Decode(s, size, "raw_unicode_escape", errors);
+ // return _PyUnicode_DecodeRawUnicodeEscapeStateful(s, size, errors, NULL);
}
-
+#if 0 // GraalPy change
PyObject *
PyUnicode_AsRawUnicodeEscapeString(PyObject *unicode)
{
diff --git a/graalpython/com.oracle.graal.python.frozen/freeze_modules.py b/graalpython/com.oracle.graal.python.frozen/freeze_modules.py
index c08d654cd6..11212e9233 100644
--- a/graalpython/com.oracle.graal.python.frozen/freeze_modules.py
+++ b/graalpython/com.oracle.graal.python.frozen/freeze_modules.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2021, 2024, Oracle and/or its affiliates.
+# Copyright (c) 2021, 2025, Oracle and/or its affiliates.
# Copyright (C) 1996-2020 Python Software Foundation
#
# Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -105,13 +105,15 @@ def add_graalpython_core():
l.append("polyglot.arrow : polyglot.arrow = " + os.path.join(lib_graalpython, "modules/_polyglot_arrow.py"))
for name in [
"modules/_sysconfigdata",
+ "modules/_polyglot",
+ "modules/_polyglot_datetime",
+ "modules/_polyglot_time",
]:
modname = os.path.basename(name)
modpath = os.path.join(lib_graalpython, f"{name}.py")
l.append(f"{modname} : {modname} = {modpath}")
for name in [
"__graalpython__",
- "_polyglot",
"_sre",
"_sysconfig",
"_weakref",
@@ -119,6 +121,7 @@ def add_graalpython_core():
"java",
"pip_hook",
"unicodedata",
+ "_nt",
]:
modname = f"graalpy.{os.path.basename(name)}"
modpath = os.path.join(lib_graalpython, f"{name}.py")
@@ -495,7 +498,7 @@ def lower_camel_case(str):
# write frozen files
FROZEN_MODULES_HEADER = """/*
- * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2025, 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
@@ -588,19 +591,27 @@ def write_frozen_module_file(file, modules):
if os.path.exists(file):
with open(file, "r", encoding="utf-8", newline=os.linesep) as f:
content = f.read()
+ if os.linesep != "\n":
+ if content.replace(os.linesep, "\n") == content:
+ # Windows file has Unix line endings
+ linesep = "\n"
+ else:
+ linesep = os.linesep
+ else:
+ linesep = "\n"
stat_result = os.stat(file)
atime, mtime = stat_result.st_atime, stat_result.st_mtime
else:
content = None
os.makedirs(os.path.dirname(file), exist_ok=True)
- with open(file, "w", encoding="utf-8", newline=os.linesep) as out_file:
+ with open(file, "w", encoding="utf-8", newline=linesep) as out_file:
out_file.write(FROZEN_MODULES_HEADER)
out_file.write("\n\n")
write_frozen_modules_map(out_file, modules)
out_file.write("\n")
write_frozen_lookup(out_file, modules)
out_file.write("}\n")
- with open(file, "r", encoding="utf-8", newline=os.linesep) as f:
+ with open(file, "r", encoding="utf-8", newline=linesep) as f:
new_content = f.read()
if new_content == content:
# set mtime to the old one, if we didn't change anything
diff --git a/graalpython/com.oracle.graal.python.shell/src/com/oracle/graal/python/shell/GraalPythonMain.java b/graalpython/com.oracle.graal.python.shell/src/com/oracle/graal/python/shell/GraalPythonMain.java
index f7838f7d5e..a2c0dd21f1 100644
--- a/graalpython/com.oracle.graal.python.shell/src/com/oracle/graal/python/shell/GraalPythonMain.java
+++ b/graalpython/com.oracle.graal.python.shell/src/com/oracle/graal/python/shell/GraalPythonMain.java
@@ -778,9 +778,6 @@ protected void launch(Builder contextBuilder) {
contextBuilder.option("python.IntMaxStrDigits", Integer.toString(intMaxStrDigits));
}
contextBuilder.option("python.DontWriteBytecodeFlag", Boolean.toString(dontWriteBytecode));
- if (verboseFlag) {
- contextBuilder.option("log.python.level", "INFO");
- }
contextBuilder.option("python.QuietFlag", Boolean.toString(quietFlag));
contextBuilder.option("python.NoUserSiteFlag", Boolean.toString(noUserSite));
contextBuilder.option("python.NoSiteFlag", Boolean.toString(noSite));
@@ -843,6 +840,8 @@ protected void launch(Builder contextBuilder) {
} catch (PolyglotException e) {
if (e.isExit()) {
rc = e.getExitStatus();
+ } else {
+ throw e;
}
} catch (NoSuchFileException e) {
printFileNotFoundException(e);
@@ -929,54 +928,70 @@ private void findAndApplyVenvCfg(Builder contextBuilder, String executable) {
continue;
}
String name = parts[0].trim();
- if (name.equals("home")) {
- try {
- Path homeProperty = Paths.get(parts[1].trim());
- Path graalpyHome = homeProperty;
- /*
- * (tfel): According to PEP 405, the home key is the directory of the Python
- * executable from which this virtual environment was created, that is, it
- * usually ends with "/bin" on a Unix system. On Windows, the base Python
- * should be in the top-level directory or under "\Scripts". To support
- * running from Maven artifacts where we don't have a working executable, we
- * patched our shipped venv module to set the home path without a "/bin" or
- * "\\Scripts" suffix, so we explicitly check for those two subfolder cases
- * and otherwise assume the home key is directly pointing to the Python
- * home.
- */
- if (graalpyHome.endsWith("bin") || graalpyHome.endsWith("Scripts")) {
- graalpyHome = graalpyHome.getParent();
- }
- contextBuilder.option("python.PythonHome", graalpyHome.toString());
- /*
- * First try to resolve symlinked executables, since that may be more
- * accurate than assuming the executable in 'home'.
- */
- Path baseExecutable = null;
+ switch (name) {
+ case "home":
try {
- Path realPath = executablePath.toRealPath();
- if (!realPath.equals(executablePath.toAbsolutePath())) {
- baseExecutable = realPath;
+ Path homeProperty = Paths.get(parts[1].trim());
+ Path graalpyHome = homeProperty;
+ /*
+ * (tfel): According to PEP 405, the home key is the directory of the
+ * Python executable from which this virtual environment was created,
+ * that is, it usually ends with "/bin" on a Unix system. On Windows,
+ * the base Python should be in the top-level directory or under
+ * "\Scripts". To support running from Maven artifacts where we don't
+ * have a working executable, we patched our shipped venv module to set
+ * the home path without a "/bin" or "\\Scripts" suffix, so we
+ * explicitly check for those two subfolder cases and otherwise assume
+ * the home key is directly pointing to the Python home.
+ */
+ if (graalpyHome.endsWith("bin") || graalpyHome.endsWith("Scripts")) {
+ graalpyHome = graalpyHome.getParent();
}
- } catch (IOException ex) {
- // Ignore
- }
- if (baseExecutable == null) {
- baseExecutable = homeProperty.resolve(executablePath.getFileName());
- }
- if (Files.exists(baseExecutable)) {
- contextBuilder.option("python.BaseExecutable", baseExecutable.toString());
+ contextBuilder.option("python.PythonHome", graalpyHome.toString());
/*
- * This is needed to support the legacy GraalVM layout where the
- * executable is a symlink into the 'languages' directory.
+ * First try to resolve symlinked executables, since that may be more
+ * accurate than assuming the executable in 'home'.
*/
- contextBuilder.option("python.PythonHome", baseExecutable.getParent().getParent().toString());
+ Path baseExecutable = null;
+ try {
+ Path realPath = executablePath.toRealPath();
+ if (!realPath.equals(executablePath.toAbsolutePath())) {
+ baseExecutable = realPath;
+ }
+ } catch (IOException ex) {
+ // Ignore
+ }
+ if (baseExecutable == null) {
+ baseExecutable = homeProperty.resolve(executablePath.getFileName());
+ }
+ if (Files.exists(baseExecutable)) {
+ contextBuilder.option("python.BaseExecutable", baseExecutable.toString());
+ /*
+ * This is needed to support the legacy GraalVM layout where the
+ * executable is a symlink into the 'languages' directory.
+ */
+ contextBuilder.option("python.PythonHome", baseExecutable.getParent().getParent().toString());
+ }
+ } catch (NullPointerException | InvalidPathException ex) {
+ // NullPointerException covers the possible null result of getParent()
+ warn("Could not set PYTHONHOME according to the pyvenv.cfg file.");
}
- } catch (NullPointerException | InvalidPathException ex) {
- // NullPointerException covers the possible null result of getParent()
- warn("Could not set PYTHONHOME according to the pyvenv.cfg file.");
- }
- break;
+ break;
+ case "venvlauncher_command":
+ if (!hasContextOptionSetViaCommandLine("VenvlauncherCommand")) {
+ contextBuilder.option("python.VenvlauncherCommand", parts[1].trim());
+ }
+ break;
+ case "base-prefix":
+ if (!hasContextOptionSetViaCommandLine("SysBasePrefix")) {
+ contextBuilder.option("python.SysBasePrefix", parts[1].trim());
+ }
+ break;
+ case "base-executable":
+ if (!hasContextOptionSetViaCommandLine("BaseExecutable")) {
+ contextBuilder.option("python.BaseExecutable", parts[1].trim());
+ }
+ break;
}
}
} catch (IOException ex) {
diff --git a/graalpython/com.oracle.graal.python.test.integration/pom.xml b/graalpython/com.oracle.graal.python.test.integration/pom.xml
index bad3f27502..6251cedd21 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.0.0
+ 24.2.1
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tuple.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tuple.py
index 3570a698fb..c696d5828f 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tuple.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tuple.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2025, 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
@@ -151,14 +151,15 @@ class TestPyTuple(CPyExtTestCase):
((1, 2, 3), -1, []),
((1, 2, 3), 3, str),
),
- code="""PyObject* wrap_PyTuple_SetItem(PyObject* original, Py_ssize_t index, PyObject* value) {
+ code="""
+ PyObject* wrap_PyTuple_SetItem(PyObject* original, Py_ssize_t index, PyObject* value) {
Py_ssize_t size = PyTuple_Size(original);
if (size < 0)
return NULL;
PyObject* tuple = PyTuple_New(size);
if (!tuple)
return NULL;
- for (int i = 0; i < size; i++) {
+ for (int i = 0; i < size / 2; i++) {
PyObject* item = PyTuple_GetItem(original, i);
if (!item) {
Py_DECREF(tuple);
@@ -167,6 +168,22 @@ class TestPyTuple(CPyExtTestCase):
Py_INCREF(item);
PyTuple_SET_ITEM(tuple, i, item);
}
+
+ #ifdef GRAALVM_PYTHON
+ // test that we also have it as API function on GraalPy
+ #undef PyTuple_SET_ITEM
+ #endif
+
+ for (int i = size / 2; i < size; i++) {
+ PyObject* item = PyTuple_GetItem(original, i);
+ if (!item) {
+ Py_DECREF(tuple);
+ return NULL;
+ }
+ Py_INCREF(item);
+ PyTuple_SET_ITEM(tuple, i, item);
+ }
+
Py_INCREF(value);
if (PyTuple_SetItem(tuple, index, value) < 0) {
Py_DECREF(tuple);
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/standalone/gradle/build/build.gradle b/graalpython/com.oracle.graal.python.test/src/tests/standalone/gradle/build/build.gradle
index c9b31ffe75..2dfdd3f5ce 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/standalone/gradle/build/build.gradle
+++ b/graalpython/com.oracle.graal.python.test/src/tests/standalone/gradle/build/build.gradle
@@ -1,6 +1,6 @@
plugins {
id "application"
- id 'org.graalvm.python' version '25.0.0'
+ id 'org.graalvm.python' version '24.2.1'
id "org.graalvm.buildtools.native" version "0.10.2"
}
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/standalone/gradle/build/build.gradle.kts b/graalpython/com.oracle.graal.python.test/src/tests/standalone/gradle/build/build.gradle.kts
index ee72dfe3de..8ccf16421d 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/standalone/gradle/build/build.gradle.kts
+++ b/graalpython/com.oracle.graal.python.test/src/tests/standalone/gradle/build/build.gradle.kts
@@ -1,6 +1,6 @@
plugins {
application
- id("org.graalvm.python") version "25.0.0"
+ id("org.graalvm.python") version "24.2.1"
id("org.graalvm.buildtools.native") version "0.10.2"
}
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/EmptyPIPComments.j b/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/EmptyPIPComments.j
index 098695dccb..08561a0c15 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/EmptyPIPComments.j
+++ b/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/EmptyPIPComments.j
@@ -1,6 +1,6 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
-//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:25.0.0}
+//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:24.2.1}
//PIP
// one blank after PIP
//PIP
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/EmptyPythonResourceComment.j b/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/EmptyPythonResourceComment.j
index 89f5758c5e..31330e96fd 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/EmptyPythonResourceComment.j
+++ b/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/EmptyPythonResourceComment.j
@@ -1,6 +1,6 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
-//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:25.0.0}
+//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:24.2.1}
//PYTHON_RESOURCES_DIRECTORY
public class EmptyPythonResourceComment {
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/EmptyPythonResourceCommentWithBlanks.j b/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/EmptyPythonResourceCommentWithBlanks.j
index fcb93bdc33..6f71f88213 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/EmptyPythonResourceCommentWithBlanks.j
+++ b/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/EmptyPythonResourceCommentWithBlanks.j
@@ -1,6 +1,6 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
-//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:25.0.0}
+//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:24.2.1}
// resource dir with blanks
//PYTHON_RESOURCES_DIRECTORY
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/NoPackagesResourcesDir.j b/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/NoPackagesResourcesDir.j
index c2255aca6c..24a4866b26 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/NoPackagesResourcesDir.j
+++ b/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/NoPackagesResourcesDir.j
@@ -1,6 +1,6 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
-//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:25.0.0}
+//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:24.2.1}
//PYTHON_RESOURCES_DIRECTORY python-resources
public class NoPackagesResourcesDir {
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/TwoPythonResourceComments.j b/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/TwoPythonResourceComments.j
index 3bd4381f51..ab31f0a738 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/TwoPythonResourceComments.j
+++ b/graalpython/com.oracle.graal.python.test/src/tests/standalone/jbang/TwoPythonResourceComments.j
@@ -1,6 +1,6 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
-//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:25.0.0}
+//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:24.2.1}
//PYTHON_RESOURCES_DIRECTORY
//PYTHON_RESOURCES_DIRECTORY
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/standalone/test_gradle_plugin.py b/graalpython/com.oracle.graal.python.test/src/tests/standalone/test_gradle_plugin.py
index 2b15001db5..b50be878dc 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/standalone/test_gradle_plugin.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/standalone/test_gradle_plugin.py
@@ -133,6 +133,8 @@ def check_gradle_generated_app(self, community):
cmd = gradlew_cmd + ["build"]
out, return_code = util.run_cmd(cmd, self.env, cwd=target_dir, logger=log)
util.check_ouput("BUILD SUCCESS", out, logger=log)
+ util.check_ouput("Virtual filesystem is deployed to default resources directory", out, logger=log)
+ util.check_ouput("This can cause conflicts if used with other Java libraries that also deploy GraalPy virtual filesystem", out, logger=log)
self.check_filelist(target_dir, log)
cmd = gradlew_cmd + ["nativeCompile"]
@@ -413,7 +415,7 @@ def check_gradle_python_resources_dir_and_external_dir_error(self):
gradle_cmd = util.get_gradle_wrapper(target_dir, self.env)
cmd = gradle_cmd + ["graalPyResources"]
out, return_code = util.run_cmd(cmd, self.env, cwd=target_dir)
- util.check_ouput("Cannot set both 'externalDirectory' and 'resourcesDirectory' at the same time", out)
+ util.check_ouput("Cannot set both 'externalDirectory' and 'resourceDirectory' at the same time", out)
assert return_code != 0, out
@@ -478,6 +480,8 @@ def check_gradle_namespaced_vfs(self):
app1_gradle_cmd = util.get_gradle_wrapper(app1_dir, self.env)
out, return_code = util.run_cmd(app1_gradle_cmd + ['publishToMavenLocal'], self.env, cwd=app1_dir)
+ util.check_ouput("Virtual filesystem is deployed to default resources directory", out, contains=False)
+ util.check_ouput("This can cause conflicts if used with other Java libraries that also deploy GraalPy virtual filesystem", out, contains=False)
assert return_code == 0, out
app2_gradle_cmd = util.get_gradle_wrapper(app2_dir, self.env)
@@ -677,11 +681,11 @@ def setUpClass(cls):
cls.build_file_name = "build.gradle.kts"
cls.settings_file_name = "settings.gradle.kts"
- @unittest.skipUnless(util.is_gradle_plugin_test_enabled, "ENABLE_GRADLE_PLUGIN_UNITTESTS is not true")
+ @unittest.skipUnless(util.is_gradle_plugin_long_running_test_enabled, "ENABLE_GRADLE_PLUGIN_LONG_RUNNING_UNITTESTS is not true")
def test_gradle_generated_app(self):
self.check_gradle_generated_app(community=True)
- @unittest.skipUnless(util.is_gradle_plugin_test_enabled, "ENABLE_GRADLE_PLUGIN_UNITTESTS is not true")
+ @unittest.skipUnless(util.is_gradle_plugin_long_running_test_enabled, "ENABLE_GRADLE_PLUGIN_LONG_RUNNING_UNITTESTS is not true")
def test_gradle_generated_app_external_resources(self):
self.check_gradle_generated_app_external_resources()
@@ -701,7 +705,7 @@ def test_gradle_check_home(self):
def test_gradle_empty_packages(self):
self.check_gradle_empty_packages()
- @unittest.skipUnless(util.is_gradle_plugin_test_enabled, "ENABLE_GRADLE_PLUGIN_UNITTESTS is not true")
+ @unittest.skipUnless(util.is_gradle_plugin_long_running_test_enabled, "ENABLE_GRADLE_PLUGIN_LONG_RUNNING_UNITTESTS is not true")
def test_gradle_namespaced_vfs(self):
self.check_gradle_namespaced_vfs()
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/standalone/test_maven_plugin.py b/graalpython/com.oracle.graal.python.test/src/tests/standalone/test_maven_plugin.py
index 4b2cdb5c04..5c4f9e34cb 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/standalone/test_maven_plugin.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/standalone/test_maven_plugin.py
@@ -115,6 +115,8 @@ def check_generated_app(self, use_default_vfs_path, use_utils_pkg=False):
cmd = mvnw_cmd + ["package", "-Pnative", "-DmainClass=it.pkg.GraalPy"]
out, return_code = util.run_cmd(cmd, self.env, cwd=target_dir)
util.check_ouput("BUILD SUCCESS", out)
+ util.check_ouput("Virtual filesystem is deployed to default resources directory", out, contains=use_default_vfs_path)
+ util.check_ouput("This can cause conflicts if used with other Java libraries that also deploy GraalPy virtual filesystem.", out, contains=use_default_vfs_path)
# check fileslist.txt
fl_path = os.path.join(target_dir, "target", "classes", vfs_prefix, "fileslist.txt")
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_dict.py b/graalpython/com.oracle.graal.python.test/src/tests/test_dict.py
index c30e983979..d56456c79c 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/test_dict.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/test_dict.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2025, 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
@@ -1267,3 +1267,13 @@ def test_dict_values_eq():
d1 = {1: 1, 2: 2, 4: 4}
assert d1.values() != d1.values()
+def test_removing_attr_from_economic_map():
+ class Test:
+ pass
+
+ o = Test()
+ o.foo = 1
+ o.__dict__[42] = 10
+ del o.foo
+
+ assert "foo" not in o.__dict__
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py
index 8533ed3835..6ea5c7e6f6 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py
+++ b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2025, 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
@@ -150,7 +150,7 @@ def t(obj):
# ForeignInstantiable
self.assertEqual(t((e for e in [1])), polyglot.ForeignIteratorIterable)
self.assertEqual(t(iter([1])), polyglot.ForeignIteratorIterable)
- self.assertEqual(t(object), polyglot.ForeignExecutableClass)
+ self.assertEqual(t(object), polyglot.ForeignClassExecutable)
self.assertEqual(t(None), polyglot.ForeignNone)
self.assertEqual(t(1), polyglot.ForeignNumber)
self.assertEqual(t("abc"), polyglot.ForeignString)
@@ -471,10 +471,19 @@ def test_java_import_from_jar(self):
os.unlink(tempname)
def test_java_class(self):
- from java.lang import Integer, Number, NumberFormatException
- self.assertEqual(type(Integer).mro(), [polyglot.ForeignClass, polyglot.ForeignInstantiable, polyglot.ForeignAbstractClass, polyglot.ForeignObject, object])
+ from java.lang import Number, NumberFormatException
+ from java.util import ArrayList
+ self.assertEqual(type(ArrayList).mro(), [polyglot.ForeignClass, polyglot.ForeignAbstractClass, polyglot.ForeignInstantiable, polyglot.ForeignObject, object])
self.assertEqual(type(Number).mro(), [polyglot.ForeignAbstractClass, polyglot.ForeignObject, object])
- self.assertEqual(type(NumberFormatException).mro(), [polyglot.ForeignClass, polyglot.ForeignInstantiable, polyglot.ForeignAbstractClass, polyglot.ForeignObject, object])
+ self.assertEqual(type(NumberFormatException).mro(), [polyglot.ForeignClass, polyglot.ForeignAbstractClass, polyglot.ForeignInstantiable, polyglot.ForeignObject, object])
+
+ from java.util import ArrayList
+ l = ArrayList()
+ assert isinstance(l, ArrayList)
+ self.assertEqual(getattr(ArrayList, 'class'), l.getClass())
+
+ with self.assertRaisesRegex(TypeError, "ForeignInstantiable.__call__\(\) got an unexpected keyword argument 'kwarg'"):
+ ArrayList(kwarg=42)
def test_java_exceptions(self):
# TODO: more tests
@@ -1040,7 +1049,9 @@ def test_java_map(self):
h.__init__(a=1, b=2)
assert h == {'a': 1, 'b': 2}
- with self.assertRaisesRegex(TypeError, 'invalid instantiation of foreign object'):
+ # Because it tries to call ForeignDict.__call__, but ForeignDict is not executable/instantiable,
+ # so it resolves to type.__call__, which cannot create a ForeignDict
+ with self.assertRaisesRegex(TypeError, "descriptor requires a 'dict' object but received a 'ForeignDict'"):
type(h).fromkeys(['a', 'b'], 42)
def test_java_iterator(self):
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_startup.py b/graalpython/com.oracle.graal.python.test/src/tests/test_startup.py
new file mode 100644
index 0000000000..b17986a53c
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.test/src/tests/test_startup.py
@@ -0,0 +1,90 @@
+# Copyright (c) 2025, 2025, 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import unittest
+import sys
+import re
+import subprocess
+import platform
+
+# Both lists should remain as small as possible to avoid adding overhead to startup
+expected_nosite_startup_modules = [
+ '_frozen_importlib',
+ '_frozen_importlib_external',
+ 'builtins',
+ '__graalpython__',
+ '_weakref',
+ 'unicodedata',
+ '_sre',
+ '_sysconfig',
+ 'java',
+ 'pip_hook',
+] + (['_nt'] if platform.system() == 'Windows' else [])
+
+expected_full_startup_modules = expected_nosite_startup_modules + [
+ '_abc',
+ 'types',
+ '_weakrefset',
+ '_py_abc',
+ 'abc',
+ 'stat',
+ '_collections_abc',
+ 'genericpath',
+ *(['_winapi', 'ntpath'] if platform.system() == 'Windows' else ['posixpath']),
+ 'os',
+ '_sitebuiltins',
+ '_io',
+ 'io',
+ 'site',
+]
+
+class StartupTests(unittest.TestCase):
+ @unittest.skipUnless(sys.implementation.name == 'graalpy', "GraalPy-specific test")
+ def test_startup_nosite(self):
+ result = subprocess.check_output([sys.executable, '--log.level=FINE', '-S', '-v', '-c', 'print("Hello")'], stderr=subprocess.STDOUT, text=True)
+ assert 'Hello' in result
+ imports = re.findall("import '(\S+)'", result)
+ self.assertEqual(expected_nosite_startup_modules, imports)
+
+ @unittest.skipUnless(sys.implementation.name == 'graalpy', "GraalPy-specific test")
+ def test_startup_full(self):
+ result = subprocess.check_output([sys.executable, '--log.level=FINE', '-s', '-v', '-c', 'print("Hello")'], stderr=subprocess.STDOUT, text=True)
+ assert 'Hello' in result
+ imports = re.findall("import '(\S+)'", result)
+ self.assertEqual(expected_full_startup_modules, imports)
diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_strptime.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_strptime.txt
index 019ffaf499..220ef38651 100644
--- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_strptime.txt
+++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_strptime.txt
@@ -35,7 +35,8 @@ test.test_strptime.StrptimeTests.test_percent @ darwin-arm64,darwin-x86_64,linux
test.test_strptime.StrptimeTests.test_second @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-x86_64,win32-AMD64
test.test_strptime.StrptimeTests.test_strptime_exception_context @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-x86_64,win32-AMD64
test.test_strptime.StrptimeTests.test_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-x86_64,win32-AMD64
-test.test_strptime.StrptimeTests.test_timezone @ darwin-arm64,darwin-x86_64,win32-AMD64
+# Seems to be dependent on the actual time/date/timezone of the machine, at least on GraalPy. Needs investigation
+!test.test_strptime.StrptimeTests.test_timezone
test.test_strptime.StrptimeTests.test_unconverteddata @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-x86_64,win32-AMD64
test.test_strptime.StrptimeTests.test_year @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-x86_64,win32-AMD64
test.test_strptime.TimeRETests.test_blankpattern @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-x86_64,win32-AMD64
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java
index 633acb7e4c..6115cfbd48 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java
@@ -59,6 +59,9 @@
import java.util.ServiceLoader;
import java.util.logging.Level;
+import com.oracle.graal.python.builtins.objects.foreign.ForeignExecutableBuiltins;
+import com.oracle.graal.python.builtins.objects.foreign.ForeignInstantiableBuiltins;
+import com.oracle.graal.python.builtins.objects.foreign.ForeignIterableBuiltins;
import org.graalvm.nativeimage.ImageInfo;
import com.oracle.graal.python.PythonLanguage;
@@ -245,6 +248,7 @@
import com.oracle.graal.python.builtins.objects.dict.PDict;
import com.oracle.graal.python.builtins.objects.ellipsis.EllipsisBuiltins;
import com.oracle.graal.python.builtins.objects.enumerate.EnumerateBuiltins;
+import com.oracle.graal.python.builtins.objects.exception.AttributeErrorBuiltins;
import com.oracle.graal.python.builtins.objects.exception.BaseExceptionBuiltins;
import com.oracle.graal.python.builtins.objects.exception.BaseExceptionGroupBuiltins;
import com.oracle.graal.python.builtins.objects.exception.ImportErrorBuiltins;
@@ -259,6 +263,7 @@
import com.oracle.graal.python.builtins.objects.exception.UnicodeTranslateErrorBuiltins;
import com.oracle.graal.python.builtins.objects.floats.FloatBuiltins;
import com.oracle.graal.python.builtins.objects.floats.PFloat;
+import com.oracle.graal.python.builtins.objects.foreign.ForeignAbstractClassBuiltins;
import com.oracle.graal.python.builtins.objects.foreign.ForeignBooleanBuiltins;
import com.oracle.graal.python.builtins.objects.foreign.ForeignNumberBuiltins;
import com.oracle.graal.python.builtins.objects.foreign.ForeignObjectBuiltins;
@@ -419,6 +424,11 @@ private static TruffleString[] initializeCoreFiles() {
toTruffleStringUncached("_sysconfig"),
toTruffleStringUncached("java"),
toTruffleStringUncached("pip_hook")));
+ if (PythonOS.getPythonOS() == PythonOS.PLATFORM_WIN32) {
+ coreFiles = new ArrayList<>(coreFiles);
+ coreFiles.add(toTruffleStringUncached("_nt"));
+ }
+
// add service loader defined python file extensions
if (!ImageInfo.inImageRuntimeCode()) {
ServiceLoader providers = ServiceLoader.load(PythonBuiltins.class, Python3Core.class.getClassLoader());
@@ -487,6 +497,10 @@ private static PythonBuiltins[] initializeBuiltins(boolean nativeAccessAllowed,
new ForeignObjectBuiltins(),
new ForeignNumberBuiltins(),
new ForeignBooleanBuiltins(),
+ new ForeignAbstractClassBuiltins(),
+ new ForeignExecutableBuiltins(),
+ new ForeignInstantiableBuiltins(),
+ new ForeignIterableBuiltins(),
new ListBuiltins(),
new DictBuiltins(),
new DictReprBuiltin(),
@@ -563,6 +577,7 @@ private static PythonBuiltins[] initializeBuiltins(boolean nativeAccessAllowed,
new GenericAliasIteratorBuiltins(),
new com.oracle.graal.python.builtins.objects.types.UnionTypeBuiltins(),
// exceptions
+ new AttributeErrorBuiltins(),
new SystemExitBuiltins(),
new ImportErrorBuiltins(),
new StopIterationBuiltins(),
@@ -1056,9 +1071,6 @@ public void run() {
}
}
- // import polyglot decorators and special interop predefined behavior
- loadFile(toTruffleStringUncached("_polyglot"), getContext().getCoreHomeOrFail());
-
initialized = true;
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java
index cc3d1de44a..02affaa5cd 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java
@@ -302,8 +302,12 @@ public enum PythonBuiltinClassType implements TruffleObject {
// Foreign
ForeignObject("ForeignObject", J_POLYGLOT, Flags.PUBLIC_BASE_WDICT, ForeignObjectBuiltins.SLOTS),
- ForeignNumber("ForeignNumber", J_POLYGLOT, Flags.PUBLIC_BASE_WDICT, FOREIGNNUMBER_M_FLAGS, ForeignNumberBuiltins.SLOTS),
- ForeignBoolean("ForeignBoolean", J_POLYGLOT, Flags.PUBLIC_BASE_WDICT, FOREIGNNUMBER_M_FLAGS, ForeignBooleanBuiltins.SLOTS),
+ ForeignNumber("ForeignNumber", J_POLYGLOT, ForeignObject, Flags.PUBLIC_BASE_WDICT, FOREIGNNUMBER_M_FLAGS, ForeignNumberBuiltins.SLOTS),
+ ForeignBoolean("ForeignBoolean", J_POLYGLOT, ForeignNumber, Flags.PUBLIC_BASE_WDICT, FOREIGNNUMBER_M_FLAGS, ForeignBooleanBuiltins.SLOTS),
+ ForeignAbstractClass("ForeignAbstractClass", J_POLYGLOT, ForeignObject, Flags.PUBLIC_BASE_WDICT),
+ ForeignExecutable("ForeignExecutable", J_POLYGLOT, ForeignObject, Flags.PUBLIC_BASE_WDICT),
+ ForeignInstantiable("ForeignInstantiable", J_POLYGLOT, ForeignObject, Flags.PUBLIC_BASE_WDICT),
+ ForeignIterable("ForeignIterable", J_POLYGLOT, ForeignObject, Flags.PUBLIC_BASE_WDICT),
// bz2
BZ2Compressor("BZ2Compressor", "_bz2"),
@@ -332,6 +336,7 @@ public enum PythonBuiltinClassType implements TruffleObject {
PStringIO("StringIO", "_io", Flags.PUBLIC_BASE_WDICT),
PBytesIO("BytesIO", "_io", Flags.PUBLIC_BASE_WDICT),
PBytesIOBuf("_BytesIOBuffer", "_io", Flags.PRIVATE_BASE_WODICT),
+ PWindowsConsoleIO("_WindowsConsoleIO", "_io", Flags.PRIVATE_BASE_WODICT),
PStatResult("stat_result", "os", Flags.PUBLIC_DERIVED_WODICT, TUPLE_M_FLAGS),
PStatvfsResult("statvfs_result", "os", Flags.PUBLIC_DERIVED_WODICT, TUPLE_M_FLAGS),
@@ -627,6 +632,11 @@ private static class Flags {
this(name, module, module, flags);
}
+ PythonBuiltinClassType(String name, String module, PythonBuiltinClassType base, Flags flags) {
+ this(name, module, module, flags);
+ this.base = base;
+ }
+
PythonBuiltinClassType(String name, String module, Flags flags, TpSlots slots) {
this(name, module, module, flags, DEFAULT_M_FLAGS, slots);
}
@@ -639,6 +649,11 @@ private static class Flags {
this(name, module, module, flags, methodsFlags, slots);
}
+ PythonBuiltinClassType(String name, String module, PythonBuiltinClassType base, Flags flags, long methodsFlags, TpSlots slots) {
+ this(name, module, module, flags, methodsFlags, slots);
+ this.base = base;
+ }
+
PythonBuiltinClassType(String name, String publishInModule, String moduleName, Flags flags) {
this(name, publishInModule, moduleName, flags, DEFAULT_M_FLAGS, TpSlots.createEmpty());
}
@@ -837,9 +852,6 @@ public final Shape getInstanceShape(PythonLanguage lang) {
Boolean.base = PInt;
- ForeignNumber.base = ForeignObject;
- ForeignBoolean.base = ForeignNumber;
-
PBaseExceptionGroup.base = PBaseException;
SystemExit.base = PBaseException;
KeyboardInterrupt.base = PBaseException;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java
index 13843d5f67..49507f1a1e 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2025, 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
@@ -113,6 +113,7 @@
import com.oracle.graal.python.nodes.interop.InteropBehaviorMethod;
import com.oracle.graal.python.nodes.interop.PForeignToPTypeNode;
import com.oracle.graal.python.nodes.object.GetForeignObjectClassNode;
+import com.oracle.graal.python.nodes.statement.AbstractImportNode;
import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
import com.oracle.graal.python.nodes.util.CannotCastException;
import com.oracle.graal.python.nodes.util.CastToJavaStringNode;
@@ -161,6 +162,7 @@ public final class PolyglotModuleBuiltins extends PythonBuiltins {
private static final TruffleString T_MODIFIABLE = tsLiteral("modifiable");
private static final TruffleString T_INVOKABLE = tsLiteral("invokable");
private static final TruffleString T_INTERNAL = tsLiteral("internal");
+ private static final TruffleString T_INTERNAL_POLYGLOT_MODULE = tsLiteral("_polyglot");
@Override
protected List> getNodeFactories() {
@@ -189,6 +191,9 @@ public void postInitialize(Python3Core core) {
super.postInitialize(core);
GetForeignObjectClassNode.getUncached().defineSingleTraitClasses();
+
+ // import polyglot decorators which are defined in Python code
+ AbstractImportNode.importModule(T_INTERNAL_POLYGLOT_MODULE);
}
@Builtin(name = "import_value", minNumOfPositionalArgs = 1, parameterNames = {"name"})
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java
index edae5c6846..2d567ece0e 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2024, Oracle and/or its affiliates.
+ * Copyright (c) 2018, 2025, Oracle and/or its affiliates.
* Copyright (c) 2014, Regents of the University of California
*
* All rights reserved.
@@ -366,15 +366,6 @@ public void postInitialize(Python3Core core) {
if (PythonOS.getPythonOS() == PythonOS.PLATFORM_WIN32) {
// XXX: Until we fix pip
environ.setItem(toTruffleStringUncached("PIP_NO_CACHE_DIR"), toTruffleStringUncached("0"));
- // XXX: Until we have working winapi and winreg modules for MSVC discovery
- environ.setItem(toTruffleStringUncached("DISTUTILS_USE_SDK"), toTruffleStringUncached("1"));
- if (getenv.get("MSSdk") == null) {
- String sdkdir = getenv.get("WindowsSdkDir");
- if (sdkdir == null) {
- sdkdir = "unset";
- }
- environ.setItem(toTruffleStringUncached("MSSdk"), toTruffleStringUncached(sdkdir));
- }
}
PythonModule posix;
if (PythonOS.getPythonOS() == PythonOS.PLATFORM_WIN32) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TimeModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TimeModuleBuiltins.java
index 46bd74525e..6eec222635 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TimeModuleBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TimeModuleBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2024, Oracle and/or its affiliates.
+ * Copyright (c) 2017, 2025, Oracle and/or its affiliates.
* Copyright (c) 2013, Regents of the University of California
*
* All rights reserved.
@@ -49,6 +49,7 @@
import java.util.List;
import java.util.TimeZone;
+import com.oracle.graal.python.nodes.statement.AbstractImportNode;
import org.graalvm.nativeimage.ImageInfo;
import com.oracle.graal.python.annotations.ArgumentClinic;
@@ -153,6 +154,7 @@ public final class TimeModuleBuiltins extends PythonBuiltins {
public static final TruffleString T_DAYLIGHT = tsLiteral("daylight");
public static final TruffleString T_TIMEZONE = tsLiteral("timezone");
public static final TruffleString T_ALTZONE = tsLiteral("altzone");
+ public static final TruffleString T_POLYGLOT_TIME = tsLiteral("_polyglot_time");
@Override
protected List extends NodeFactory extends PythonBuiltinBaseNode>> getNodeFactories() {
@@ -192,6 +194,9 @@ public void postInitialize(Python3Core core) {
int rawOffsetSeconds = defaultTimeZone.getRawOffset() / -1000;
timeModule.setAttribute(T_TIMEZONE, rawOffsetSeconds);
timeModule.setAttribute(T_ALTZONE, rawOffsetSeconds - 3600);
+
+ // register_interop_behavior() for time.struct_time
+ AbstractImportNode.importModule(T_POLYGLOT_TIME);
}
@TruffleBoundary
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextObjectBuiltins.java
index 6ee8ff9172..b14d9f45e1 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextObjectBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextObjectBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2025, 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
@@ -129,6 +129,7 @@
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
import com.oracle.graal.python.nodes.object.GetClassNode;
import com.oracle.graal.python.nodes.object.GetOrCreateDictNode;
+import com.oracle.graal.python.nodes.object.IsNode;
import com.oracle.graal.python.nodes.util.CannotCastException;
import com.oracle.graal.python.nodes.util.CastToJavaStringNode;
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
@@ -739,4 +740,13 @@ static Object getDict(Object object,
return getDict.execute(inliningTarget, object);
}
}
+
+ @CApiBuiltin(ret = Int, args = {PyObject, PyObject}, call = Ignored)
+ abstract static class PyTruffle_Is extends CApiBinaryBuiltinNode {
+ @Specialization
+ static int isTrue(Object a, Object b,
+ @Cached IsNode isNode) {
+ return isNode.execute(a, b) ? 1 : 0;
+ }
+ }
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTypeBuiltins.java
index eea71e9ebd..a997a8a2aa 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTypeBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTypeBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2025, 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
@@ -228,20 +228,24 @@ static Object doIt(PythonAbstractNativeObject clazz,
@CApiBuiltin(ret = ArgDescriptor.Void, args = {PyTypeObject}, call = Direct)
abstract static class PyType_Modified extends CApiUnaryBuiltinNode {
-
@TruffleBoundary
@Specialization
- static Object doIt(PythonAbstractNativeObject clazz,
+ static Object doIt(PythonAbstractClass object,
@Bind("this") Node inliningTarget) {
- PythonContext context = PythonContext.get(inliningTarget);
- CyclicAssumption nativeClassStableAssumption = context.getNativeClassStableAssumption(clazz, false);
- if (nativeClassStableAssumption != null) {
- nativeClassStableAssumption.invalidate("PyType_Modified(\"" + TypeNodes.GetNameNode.executeUncached(clazz).toJavaStringUncached() + "\") called");
+ if (object instanceof PythonAbstractNativeObject clazz) {
+ PythonContext context = PythonContext.get(inliningTarget);
+ CyclicAssumption nativeClassStableAssumption = context.getNativeClassStableAssumption(clazz, false);
+ if (nativeClassStableAssumption != null) {
+ nativeClassStableAssumption.invalidate("PyType_Modified(\"" + TypeNodes.GetNameNode.executeUncached(clazz).toJavaStringUncached() + "\") called");
+ }
+ MroSequenceStorage mroStorage = TypeNodes.GetMroStorageNode.executeUncached(clazz);
+ mroStorage.lookupChanged();
+ // Reload slots from native, which also invalidates cached slot lookups
+ clazz.setTpSlots(TpSlots.fromNative(clazz, context));
+ } else {
+ MroSequenceStorage mroStorage = TypeNodes.GetMroStorageNode.executeUncached(object);
+ mroStorage.lookupChanged();
}
- MroSequenceStorage mroStorage = TypeNodes.GetMroStorageNode.executeUncached(clazz);
- mroStorage.lookupChanged();
- // Reload slots from native, which also invalidates cached slot lookups
- clazz.setTpSlots(TpSlots.fromNative(clazz, context));
return PNone.NO_VALUE;
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/IOModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/IOModuleBuiltins.java
index 6cc698da4d..da35249a4e 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/IOModuleBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/IOModuleBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, 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
@@ -52,6 +52,7 @@
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.PIncrementalNewlineDecoder;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.PTextIOWrapper;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError;
+import static com.oracle.graal.python.builtins.PythonBuiltinClassType.PWindowsConsoleIO;
import static com.oracle.graal.python.builtins.modules.WarningsModuleBuiltins.T_WARN;
import static com.oracle.graal.python.builtins.modules.io.BufferedIOUtil.SEEK_CUR;
import static com.oracle.graal.python.builtins.modules.io.BufferedIOUtil.SEEK_END;
@@ -144,6 +145,7 @@ public void initialize(Python3Core core) {
addBuiltinConstant("_warn", core.lookupBuiltinModule(T__WARNINGS).getAttribute(T_WARN));
if (PythonOS.getPythonOS() == PythonOS.PLATFORM_WIN32) {
addBuiltinConstant("_os", core.lookupBuiltinModule(T_NT));
+ addBuiltinConstant("_WindowsConsoleIO", PWindowsConsoleIO);
} else {
addBuiltinConstant("_os", core.lookupBuiltinModule(T_POSIX));
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java
index 5909dc1427..97ad1090b1 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, 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
@@ -504,6 +504,7 @@ public final class CApiFunction {
@CApiBuiltin(name = "PyUnicode_DecodeLatin1", ret = PyObject, args = {ConstCharPtrAsTruffleString, Py_ssize_t, ConstCharPtrAsTruffleString}, call = CImpl)
@CApiBuiltin(name = "PyUnicode_DecodeLocale", ret = PyObject, args = {ConstCharPtrAsTruffleString, ConstCharPtrAsTruffleString}, call = CImpl)
@CApiBuiltin(name = "PyUnicode_DecodeLocaleAndSize", ret = PyObject, args = {ConstCharPtrAsTruffleString, Py_ssize_t, ConstCharPtrAsTruffleString}, call = CImpl)
+ @CApiBuiltin(name = "PyUnicode_DecodeRawUnicodeEscape", ret = PyObject, args = {ConstCharPtrAsTruffleString, Py_ssize_t, ConstCharPtrAsTruffleString}, call = CImpl)
@CApiBuiltin(name = "PyUnicode_DecodeUTF16", ret = PyObject, args = {ConstCharPtrAsTruffleString, Py_ssize_t, ConstCharPtrAsTruffleString, INT_LIST}, call = CImpl)
@CApiBuiltin(name = "PyUnicode_DecodeUTF16Stateful", ret = PyObject, args = {ConstCharPtrAsTruffleString, Py_ssize_t, ConstCharPtrAsTruffleString, INT_LIST, PY_SSIZE_T_PTR}, call = CImpl)
@CApiBuiltin(name = "PyUnicode_DecodeUTF32", ret = PyObject, args = {ConstCharPtrAsTruffleString, Py_ssize_t, ConstCharPtrAsTruffleString, INT_LIST}, call = CImpl)
@@ -942,7 +943,6 @@ public final class CApiFunction {
@CApiBuiltin(name = "PyUnicode_BuildEncodingMap", ret = PyObject, args = {PyObject}, call = NotImplemented)
@CApiBuiltin(name = "PyUnicode_CopyCharacters", ret = Py_ssize_t, args = {PyObject, Py_ssize_t, PyObject, Py_ssize_t, Py_ssize_t}, call = NotImplemented)
@CApiBuiltin(name = "PyUnicode_DecodeCharmap", ret = PyObject, args = {ConstCharPtrAsTruffleString, Py_ssize_t, PyObject, ConstCharPtrAsTruffleString}, call = NotImplemented)
- @CApiBuiltin(name = "PyUnicode_DecodeRawUnicodeEscape", ret = PyObject, args = {ConstCharPtrAsTruffleString, Py_ssize_t, ConstCharPtrAsTruffleString}, call = NotImplemented)
@CApiBuiltin(name = "PyUnicode_DecodeUTF7", ret = PyObject, args = {ConstCharPtrAsTruffleString, Py_ssize_t, ConstCharPtrAsTruffleString}, call = NotImplemented)
@CApiBuiltin(name = "PyUnicode_DecodeUTF7Stateful", ret = PyObject, args = {ConstCharPtrAsTruffleString, Py_ssize_t, ConstCharPtrAsTruffleString, PY_SSIZE_T_PTR}, call = NotImplemented)
@CApiBuiltin(name = "PyUnicode_DecodeUnicodeEscape", ret = PyObject, args = {ConstCharPtrAsTruffleString, Py_ssize_t, ConstCharPtrAsTruffleString}, call = NotImplemented)
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionNodes.java
index 18528ad4a6..3111ccef04 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionNodes.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, 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
@@ -1309,7 +1309,7 @@ public MethNoargsRoot(PythonLanguage language, TruffleString name, boolean isSta
@Override
protected Object[] prepareCArguments(VirtualFrame frame) {
- return new Object[]{readSelf(frame), PNone.NONE};
+ return new Object[]{readSelf(frame), PNone.NO_VALUE};
}
@Override
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingCollectionNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingCollectionNodes.java
index 55c4644dbe..ac120a2e3f 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingCollectionNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingCollectionNodes.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2025, 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
@@ -61,6 +61,7 @@
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
@@ -121,8 +122,8 @@ abstract static class SetValueHashingStorageNode extends PNodeWithContext {
@Specialization
static HashingStorage doEconomicStorage(VirtualFrame frame, Node inliningTarget, EconomicMapStorage map, Object value,
- @Cached ObjectHashMap.PutNode putNode,
- @Cached InlinedLoopConditionProfile loopProfile) {
+ @Shared("putNode") @Cached ObjectHashMap.PutNode putNode,
+ @Shared("loopProfile") @Cached InlinedLoopConditionProfile loopProfile) {
// We want to avoid calling __hash__() during map.put
map.setValueForAllKeys(frame, inliningTarget, value, putNode, loopProfile);
return map;
@@ -134,14 +135,25 @@ static HashingStorage doGeneric(VirtualFrame frame, Node inliningTarget, Hashing
@Cached HashingStorageSetItem setItem,
@Cached HashingStorageGetIterator getIterator,
@Cached HashingStorageIteratorNext itNext,
- @Cached HashingStorageIteratorKey itKey) {
+ @Cached HashingStorageIteratorKey itKey,
+ @Shared("putNode") @Cached ObjectHashMap.PutNode putNode,
+ @Shared("loopProfile") @Cached InlinedLoopConditionProfile loopProfile) {
HashingStorageIterator it = getIterator.execute(inliningTarget, map);
- HashingStorage storage = map;
while (itNext.execute(inliningTarget, map, it)) {
- Object key = itKey.execute(inliningTarget, storage, it);
- storage = setItem.execute(frame, inliningTarget, storage, key, value);
+ Object key = itKey.execute(inliningTarget, map, it);
+ HashingStorage newStorage = setItem.execute(frame, inliningTarget, map, key, value);
+ if (newStorage != map) {
+ // when the storage changes, the iterator state is not a reliable cursor
+ // anymore and we need to restart.
+ if (newStorage instanceof EconomicMapStorage mapStorage) {
+ mapStorage.setValueForAllKeys(frame, inliningTarget, value, putNode, loopProfile);
+ return mapStorage;
+ } else {
+ throw CompilerDirectives.shouldNotReachHere("We only generalize to EconomicMapStorage");
+ }
+ }
}
- return storage;
+ return map;
}
protected static boolean isEconomicMapStorage(Object o) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/AttributeErrorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/AttributeErrorBuiltins.java
new file mode 100644
index 0000000000..02dfc38dd8
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/AttributeErrorBuiltins.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2025, 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
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.graal.python.builtins.objects.exception;
+
+import static com.oracle.graal.python.nodes.SpecialMethodNames.J___GETSTATE__;
+import static com.oracle.graal.python.nodes.SpecialMethodNames.J___INIT__;
+import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REDUCE__;
+import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
+import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
+
+import java.util.List;
+
+import com.oracle.graal.python.builtins.Builtin;
+import com.oracle.graal.python.builtins.CoreFunctions;
+import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.PythonBuiltins;
+import com.oracle.graal.python.builtins.objects.PNone;
+import com.oracle.graal.python.builtins.objects.common.EmptyStorage;
+import com.oracle.graal.python.builtins.objects.common.HashingStorage;
+import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
+import com.oracle.graal.python.builtins.objects.dict.PDict;
+import com.oracle.graal.python.builtins.objects.function.PKeyword;
+import com.oracle.graal.python.nodes.ErrorMessages;
+import com.oracle.graal.python.nodes.PRaiseNode;
+import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
+import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
+import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
+import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode;
+import com.oracle.graal.python.nodes.object.GetClassNode;
+import com.oracle.graal.python.nodes.object.GetDictIfExistsNode;
+import com.oracle.graal.python.runtime.object.PythonObjectFactory;
+import com.oracle.truffle.api.dsl.Bind;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.GenerateNodeFactory;
+import com.oracle.truffle.api.dsl.NodeFactory;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.InlinedLoopConditionProfile;
+import com.oracle.truffle.api.strings.TruffleString;
+
+@CoreFunctions(extendClasses = PythonBuiltinClassType.AttributeError)
+public final class AttributeErrorBuiltins extends PythonBuiltins {
+
+ @Override
+ protected List extends NodeFactory extends PythonBuiltinBaseNode>> getNodeFactories() {
+ return AttributeErrorBuiltinsFactory.getFactories();
+ }
+
+ private static final int IDX_NAME = 0;
+ private static final int IDX_OBJ = 1;
+ private static final int NUM_ATTRS = IDX_OBJ + 1;
+
+ private static final TruffleString T_NAME = tsLiteral("name");
+ private static final TruffleString T_OBJ = tsLiteral("obj");
+
+ private static final BaseExceptionAttrNode.StorageFactory ATTR_FACTORY = (args, factory) -> new Object[NUM_ATTRS];
+
+ @Builtin(name = J___INIT__, minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true)
+ @GenerateNodeFactory
+ abstract static class InitNode extends PythonVarargsBuiltinNode {
+
+ @Specialization
+ static Object init(PBaseException self, Object[] args, PKeyword[] kwargs,
+ @Bind Node inliningTarget,
+ @Cached BaseExceptionBuiltins.BaseExceptionInitNode baseExceptionInitNode,
+ @Cached TruffleString.EqualNode equalNameNode,
+ @Cached TruffleString.EqualNode equalObjNode,
+ @Cached InlinedLoopConditionProfile loopProfile,
+ @Cached PRaiseNode raiseNode) {
+ baseExceptionInitNode.execute(self, args);
+ Object[] attrs = new Object[NUM_ATTRS];
+ loopProfile.profileCounted(inliningTarget, kwargs.length);
+ for (int i = 0; loopProfile.inject(inliningTarget, i < kwargs.length); i++) {
+ PKeyword kw = kwargs[i];
+ TruffleString kwName = kw.getName();
+ if (equalNameNode.execute(kwName, T_NAME, TS_ENCODING)) {
+ attrs[IDX_NAME] = kw.getValue();
+ } else if (equalObjNode.execute(kwName, T_OBJ, TS_ENCODING)) {
+ attrs[IDX_OBJ] = kw.getValue();
+ } else {
+ throw raiseNode.raise(PythonBuiltinClassType.TypeError, ErrorMessages.S_IS_AN_INVALID_ARG_FOR_S, kw.getName(), "AttributeError");
+ }
+ }
+ self.setExceptionAttributes(attrs);
+ return PNone.NONE;
+ }
+ }
+
+ @Builtin(name = "name", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "attribute name")
+ @GenerateNodeFactory
+ public abstract static class NameNode extends PythonBinaryBuiltinNode {
+ @Specialization
+ static Object generic(PBaseException self, Object value,
+ @Cached BaseExceptionAttrNode attrNode) {
+ return attrNode.execute(self, value, IDX_NAME, ATTR_FACTORY);
+ }
+ }
+
+ @Builtin(name = "obj", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "object")
+ @GenerateNodeFactory
+ public abstract static class ObjNode extends PythonBinaryBuiltinNode {
+ @Specialization
+ static Object generic(PBaseException self, Object value,
+ @Cached BaseExceptionAttrNode attrNode) {
+ return attrNode.execute(self, value, IDX_OBJ, ATTR_FACTORY);
+ }
+ }
+
+ @Builtin(name = J___GETSTATE__, minNumOfPositionalArgs = 1)
+ @GenerateNodeFactory
+ public abstract static class GetStateNode extends PythonUnaryBuiltinNode {
+
+ @Specialization
+ static Object get(PBaseException self,
+ @Bind("this") Node inliningTarget,
+ @Cached BaseExceptionAttrNode attrNode,
+ @Cached GetDictIfExistsNode getDictIfExistsNode,
+ @Cached HashingStorageNodes.HashingStorageSetItem setHashingStorageItem,
+ @Cached HashingStorageNodes.HashingStorageCopy copyStorageNode,
+ @Cached PythonObjectFactory factory) {
+ PDict dict = getDictIfExistsNode.execute(self);
+ /*
+ * Note from CPython: We specifically are not pickling the obj attribute since there are
+ * many cases where it is unlikely to be picklable.
+ */
+ Object name = attrNode.get(self, IDX_NAME, ATTR_FACTORY);
+ if (name != null) {
+ HashingStorage storage = (dict != null) ? copyStorageNode.execute(inliningTarget, dict.getDictStorage()) : EmptyStorage.INSTANCE;
+ storage = setHashingStorageItem.execute(inliningTarget, storage, T_NAME, name);
+ return factory.createDict(storage);
+ } else if (dict != null) {
+ return dict;
+ } else {
+ return PNone.NONE;
+ }
+ }
+ }
+
+ @Builtin(name = J___REDUCE__, minNumOfPositionalArgs = 1)
+ @GenerateNodeFactory
+ public abstract static class ReduceNode extends PythonUnaryBuiltinNode {
+
+ @Specialization
+ static Object reduce(VirtualFrame frame, PBaseException self,
+ @Bind("this") Node inliningTarget,
+ @Cached GetClassNode getClassNode,
+ @Cached ExceptionNodes.GetArgsNode getArgsNode,
+ @Cached GetStateNode getStateNode,
+ @Cached PythonObjectFactory factory) {
+ Object clazz = getClassNode.execute(inliningTarget, self);
+ Object args = getArgsNode.execute(inliningTarget, self);
+ Object state = getStateNode.execute(frame, self);
+ return factory.createTuple(new Object[]{clazz, args, state});
+ }
+ }
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignAbstractClassBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignAbstractClassBuiltins.java
new file mode 100644
index 0000000000..48cfd68373
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignAbstractClassBuiltins.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2017, 2025, Oracle and/or its affiliates.
+ * Copyright (c) 2014, Regents of the University of California
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.oracle.graal.python.builtins.objects.foreign;
+
+import com.oracle.graal.python.builtins.Builtin;
+import com.oracle.graal.python.builtins.CoreFunctions;
+import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.PythonBuiltins;
+import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
+import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
+import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
+import com.oracle.graal.python.runtime.GilNode;
+import com.oracle.graal.python.runtime.object.PythonObjectFactory;
+import com.oracle.graal.python.util.PythonUtils;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.GenerateNodeFactory;
+import com.oracle.truffle.api.dsl.NodeFactory;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.InteropLibrary;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.library.CachedLibrary;
+
+import java.util.List;
+
+import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___BASES__;
+import static com.oracle.graal.python.nodes.SpecialMethodNames.J___INSTANCECHECK__;
+
+/*
+ * NOTE: We are not using IndirectCallContext here in this file
+ * because it seems unlikely that these interop messages would call back to Python
+ * and that we would also need precise frame info for that case.
+ * Adding it shouldn't hurt peak, but might be a non-trivial overhead in interpreter.
+ */
+@CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignAbstractClass)
+public final class ForeignAbstractClassBuiltins extends PythonBuiltins {
+ @Override
+ protected List extends NodeFactory extends PythonBuiltinBaseNode>> getNodeFactories() {
+ return ForeignAbstractClassBuiltinsFactory.getFactories();
+ }
+
+ @Builtin(name = J___BASES__, minNumOfPositionalArgs = 1, isGetter = true, isSetter = false)
+ @GenerateNodeFactory
+ abstract static class BasesNode extends PythonUnaryBuiltinNode {
+ @Specialization
+ static Object getBases(Object self,
+ @Cached PythonObjectFactory factory) {
+ return factory.createTuple(PythonUtils.EMPTY_OBJECT_ARRAY);
+ }
+ }
+
+ @Builtin(name = J___INSTANCECHECK__, minNumOfPositionalArgs = 2)
+ @GenerateNodeFactory
+ abstract static class InstancecheckNode extends PythonBinaryBuiltinNode {
+ @Specialization(limit = "3")
+ static Object check(Object self, Object instance,
+ @CachedLibrary("self") InteropLibrary lib,
+ @Cached GilNode gil) {
+ gil.release(true);
+ try {
+ return lib.isMetaInstance(self, instance);
+ } catch (UnsupportedMessageException e) {
+ throw CompilerDirectives.shouldNotReachHere();
+ } finally {
+ gil.acquire();
+ }
+ }
+ }
+
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignExecutableBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignExecutableBuiltins.java
new file mode 100644
index 0000000000..ea02d004c7
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignExecutableBuiltins.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2017, 2025, Oracle and/or its affiliates.
+ * Copyright (c) 2014, Regents of the University of California
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.oracle.graal.python.builtins.objects.foreign;
+
+import static com.oracle.graal.python.nodes.SpecialMethodNames.J___CALL__;
+
+import java.util.List;
+
+import com.oracle.graal.python.PythonLanguage;
+import com.oracle.graal.python.builtins.Builtin;
+import com.oracle.graal.python.builtins.CoreFunctions;
+import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.PythonBuiltins;
+import com.oracle.graal.python.nodes.ErrorMessages;
+import com.oracle.graal.python.nodes.PRaiseNode;
+import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
+import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
+import com.oracle.graal.python.nodes.interop.PForeignToPTypeNode;
+import com.oracle.graal.python.runtime.ExecutionContext.IndirectCallContext;
+import com.oracle.graal.python.runtime.GilNode;
+import com.oracle.graal.python.runtime.IndirectCallData;
+import com.oracle.graal.python.runtime.PythonContext;
+import com.oracle.graal.python.runtime.exception.PythonErrorType;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Bind;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.GenerateNodeFactory;
+import com.oracle.truffle.api.dsl.NodeFactory;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.interop.ArityException;
+import com.oracle.truffle.api.interop.InteropLibrary;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.interop.UnsupportedTypeException;
+import com.oracle.truffle.api.library.CachedLibrary;
+import com.oracle.truffle.api.nodes.Node;
+
+@CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignExecutable)
+public final class ForeignExecutableBuiltins extends PythonBuiltins {
+ @Override
+ protected List extends NodeFactory extends PythonBuiltinBaseNode>> getNodeFactories() {
+ return ForeignExecutableBuiltinsFactory.getFactories();
+ }
+
+ @Builtin(name = J___CALL__, minNumOfPositionalArgs = 1, takesVarArgs = true)
+ @GenerateNodeFactory
+ public abstract static class CallNode extends PythonBuiltinNode {
+ @Specialization
+ static Object doInteropCall(VirtualFrame frame, Object callee, Object[] arguments,
+ @SuppressWarnings("unused") @Bind("this") Node inliningTarget,
+ @Cached("createFor(this)") IndirectCallData indirectCallData,
+ @CachedLibrary(limit = "4") InteropLibrary lib,
+ @Cached PForeignToPTypeNode toPTypeNode,
+ @Cached GilNode gil,
+ @Cached PRaiseNode.Lazy raiseNode) {
+ PythonLanguage language = PythonLanguage.get(inliningTarget);
+ PythonContext context = PythonContext.get(inliningTarget);
+ try {
+ Object state = IndirectCallContext.enter(frame, language, context, indirectCallData);
+ gil.release(true);
+ try {
+ return toPTypeNode.executeConvert(lib.execute(callee, arguments));
+ } finally {
+ gil.acquire();
+ IndirectCallContext.exit(frame, language, context, state);
+ }
+ } catch (ArityException | UnsupportedTypeException e) {
+ throw raiseNode.get(inliningTarget).raise(PythonErrorType.TypeError, ErrorMessages.INVALID_INSTANTIATION_OF_FOREIGN_OBJ);
+ } catch (UnsupportedMessageException e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
+ }
+ }
+ }
+
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignInstantiableBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignInstantiableBuiltins.java
new file mode 100644
index 0000000000..d5b095145e
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignInstantiableBuiltins.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2017, 2025, Oracle and/or its affiliates.
+ * Copyright (c) 2014, Regents of the University of California
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.oracle.graal.python.builtins.objects.foreign;
+
+import static com.oracle.graal.python.nodes.SpecialMethodNames.J___CALL__;
+import static com.oracle.graal.python.nodes.SpecialMethodNames.J___NEW__;
+
+import java.util.List;
+
+import com.oracle.graal.python.PythonLanguage;
+import com.oracle.graal.python.builtins.Builtin;
+import com.oracle.graal.python.builtins.CoreFunctions;
+import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.PythonBuiltins;
+import com.oracle.graal.python.nodes.ErrorMessages;
+import com.oracle.graal.python.nodes.PRaiseNode;
+import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
+import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
+import com.oracle.graal.python.nodes.interop.PForeignToPTypeNode;
+import com.oracle.graal.python.runtime.ExecutionContext.IndirectCallContext;
+import com.oracle.graal.python.runtime.GilNode;
+import com.oracle.graal.python.runtime.IndirectCallData;
+import com.oracle.graal.python.runtime.PythonContext;
+import com.oracle.graal.python.runtime.exception.PythonErrorType;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Bind;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.GenerateNodeFactory;
+import com.oracle.truffle.api.dsl.NodeFactory;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.interop.ArityException;
+import com.oracle.truffle.api.interop.InteropLibrary;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.interop.UnsupportedTypeException;
+import com.oracle.truffle.api.library.CachedLibrary;
+import com.oracle.truffle.api.nodes.Node;
+
+@CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignInstantiable)
+public final class ForeignInstantiableBuiltins extends PythonBuiltins {
+ @Override
+ protected List extends NodeFactory extends PythonBuiltinBaseNode>> getNodeFactories() {
+ return ForeignInstantiableBuiltinsFactory.getFactories();
+ }
+
+ @Builtin(name = J___NEW__, minNumOfPositionalArgs = 1, takesVarArgs = true)
+ @Builtin(name = J___CALL__, minNumOfPositionalArgs = 1, takesVarArgs = true)
+ @GenerateNodeFactory
+ public abstract static class CallNode extends PythonBuiltinNode {
+ @Specialization
+ static Object doInteropCall(VirtualFrame frame, Object callee, Object[] arguments,
+ @SuppressWarnings("unused") @Bind("this") Node inliningTarget,
+ @Cached("createFor(this)") IndirectCallData indirectCallData,
+ @CachedLibrary(limit = "4") InteropLibrary lib,
+ @Cached PForeignToPTypeNode toPTypeNode,
+ @Cached GilNode gil,
+ @Cached PRaiseNode.Lazy raiseNode) {
+ PythonLanguage language = PythonLanguage.get(inliningTarget);
+ PythonContext context = PythonContext.get(inliningTarget);
+ try {
+ Object state = IndirectCallContext.enter(frame, language, context, indirectCallData);
+ gil.release(true);
+ try {
+ return toPTypeNode.executeConvert(lib.instantiate(callee, arguments));
+ } finally {
+ gil.acquire();
+ IndirectCallContext.exit(frame, language, context, state);
+ }
+ } catch (ArityException | UnsupportedTypeException e) {
+ throw raiseNode.get(inliningTarget).raise(PythonErrorType.TypeError, ErrorMessages.INVALID_INSTANTIATION_OF_FOREIGN_OBJ);
+ } catch (UnsupportedMessageException e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
+ }
+ }
+ }
+
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignIterableBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignIterableBuiltins.java
new file mode 100644
index 0000000000..647eee6b2f
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignIterableBuiltins.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2017, 2025, Oracle and/or its affiliates.
+ * Copyright (c) 2014, Regents of the University of California
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.oracle.graal.python.builtins.objects.foreign;
+
+import static com.oracle.graal.python.nodes.SpecialMethodNames.J___ITER__;
+
+import java.util.List;
+
+import com.oracle.graal.python.builtins.Builtin;
+import com.oracle.graal.python.builtins.CoreFunctions;
+import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.PythonBuiltins;
+import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
+import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
+import com.oracle.graal.python.nodes.interop.PForeignToPTypeNode;
+import com.oracle.graal.python.runtime.GilNode;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.GenerateNodeFactory;
+import com.oracle.truffle.api.dsl.NodeFactory;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.InteropLibrary;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.library.CachedLibrary;
+
+/*
+ * NOTE: We are not using IndirectCallContext here in this file
+ * because it seems unlikely that these interop messages would call back to Python
+ * and that we would also need precise frame info for that case.
+ * Adding it shouldn't hurt peak, but might be a non-trivial overhead in interpreter.
+ */
+@CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignIterable)
+public final class ForeignIterableBuiltins extends PythonBuiltins {
+ @Override
+ protected List extends NodeFactory extends PythonBuiltinBaseNode>> getNodeFactories() {
+ return ForeignIterableBuiltinsFactory.getFactories();
+ }
+
+ @Builtin(name = J___ITER__, minNumOfPositionalArgs = 1)
+ @GenerateNodeFactory
+ public abstract static class IterNode extends PythonUnaryBuiltinNode {
+
+ @Specialization(limit = "3")
+ static Object doGeneric(Object object,
+ @CachedLibrary("object") InteropLibrary lib,
+ @Cached PForeignToPTypeNode convertNode,
+ @Cached GilNode gil) {
+ gil.release(true);
+ try {
+ return convertNode.executeConvert(lib.getIterator(object));
+ } catch (UnsupportedMessageException e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
+ } finally {
+ gil.acquire();
+ }
+ }
+ }
+
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignObjectBuiltins.java
index 617458040f..ea386f7fed 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignObjectBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignObjectBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2024, Oracle and/or its affiliates.
+ * Copyright (c) 2017, 2025, Oracle and/or its affiliates.
* Copyright (c) 2014, Regents of the University of California
*
* All rights reserved.
@@ -29,17 +29,10 @@
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.AttributeError;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError;
import static com.oracle.graal.python.builtins.objects.str.StringUtils.simpleTruffleStringFormatUncached;
-import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___BASES__;
-import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___BASES__;
-import static com.oracle.graal.python.nodes.SpecialMethodNames.J___CALL__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___DIR__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___HASH__;
-import static com.oracle.graal.python.nodes.SpecialMethodNames.J___INSTANCECHECK__;
-import static com.oracle.graal.python.nodes.SpecialMethodNames.J___ITER__;
-import static com.oracle.graal.python.nodes.SpecialMethodNames.J___NEW__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REPR__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___STR__;
-import static com.oracle.graal.python.nodes.SpecialMethodNames.T___INSTANCECHECK__;
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
import java.util.List;
@@ -53,7 +46,6 @@
import com.oracle.graal.python.builtins.PythonBuiltins;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
-import com.oracle.graal.python.builtins.objects.function.PKeyword;
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins;
import com.oracle.graal.python.builtins.objects.object.ObjectNodes;
import com.oracle.graal.python.builtins.objects.type.TpSlots;
@@ -65,35 +57,27 @@
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
-import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
-import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
import com.oracle.graal.python.nodes.interop.PForeignToPTypeNode;
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
import com.oracle.graal.python.nodes.object.GetClassNode;
-import com.oracle.graal.python.nodes.object.IsForeignObjectNode;
import com.oracle.graal.python.nodes.util.CannotCastException;
import com.oracle.graal.python.nodes.util.CastToJavaStringNode;
-import com.oracle.graal.python.runtime.ExecutionContext.IndirectCallContext;
import com.oracle.graal.python.runtime.GilNode;
-import com.oracle.graal.python.runtime.IndirectCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonOptions;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.graal.python.runtime.exception.PythonErrorType;
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
-import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
-import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
import com.oracle.truffle.api.dsl.ImportStatic;
-import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.NodeFactory;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
@@ -108,7 +92,7 @@
import com.oracle.truffle.api.strings.TruffleString;
/*
- * NOTE: We are not using IndirectCallContext here in this file (except for CallNode)
+ * NOTE: We are not using IndirectCallContext here in this file
* because it seems unlikely that these interop messages would call back to Python
* and that we would also need precise frame info for that case.
* Adding it shouldn't hurt peak, but might be a non-trivial overhead in interpreter.
@@ -144,111 +128,6 @@ private static int hashCodeBoundary(Object self) {
}
}
- @Builtin(name = J___ITER__, minNumOfPositionalArgs = 1)
- @GenerateNodeFactory
- public abstract static class IterNode extends PythonUnaryBuiltinNode {
-
- @Specialization(limit = "3")
- static Object doGeneric(Object object,
- @Cached PRaiseNode raiseNode,
- @CachedLibrary("object") InteropLibrary lib,
- @Cached PForeignToPTypeNode convertNode,
- @Cached GilNode gil) {
- gil.release(true);
- try {
- if (lib.hasIterator(object)) {
- return convertNode.executeConvert(lib.getIterator(object));
- }
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- } finally {
- gil.acquire();
- }
- throw raiseNode.raise(TypeError, ErrorMessages.FOREIGN_OBJ_ISNT_ITERABLE);
- }
- }
-
- @Builtin(name = J___NEW__, minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true)
- @GenerateNodeFactory
- abstract static class NewNode extends PythonBuiltinNode {
- @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, callee)", "!isNoValue(callee)", "keywords.length == 0"}, limit = "1")
- static Object doInteropCall(Object callee, Object[] arguments, @SuppressWarnings("unused") PKeyword[] keywords,
- @SuppressWarnings("unused") @Bind("this") Node inliningTarget,
- @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode,
- @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PForeignToPTypeNode toPTypeNode,
- @Cached GilNode gil,
- @Cached PRaiseNode.Lazy raiseNode) {
- gil.release(true);
- try {
- Object res = lib.instantiate(callee, arguments);
- return toPTypeNode.executeConvert(res);
- } catch (ArityException | UnsupportedTypeException | UnsupportedMessageException e) {
- throw raiseNode.get(inliningTarget).raise(PythonErrorType.TypeError, ErrorMessages.INVALID_INSTANTIATION_OF_FOREIGN_OBJ);
- } finally {
- gil.acquire();
- }
- }
-
- @Fallback
- @SuppressWarnings("unused")
- static Object doGeneric(Object callee, Object arguments, Object keywords,
- @Cached PRaiseNode raiseNode) {
- throw raiseNode.raise(PythonErrorType.TypeError, ErrorMessages.INVALID_INSTANTIATION_OF_FOREIGN_OBJ);
- }
- }
-
- @Builtin(name = J___CALL__, minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true)
- @GenerateNodeFactory
- public abstract static class CallNode extends PythonBuiltinNode {
- public final Object executeWithArgs(VirtualFrame frame, Object callee, Object[] arguments) {
- return execute(frame, callee, arguments, PKeyword.EMPTY_KEYWORDS);
- }
-
- public abstract Object execute(VirtualFrame frame, Object callee, Object[] arguments, PKeyword[] keywords);
-
- @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, callee)", "!isNoValue(callee)", "keywords.length == 0"}, limit = "1")
- static Object doInteropCall(VirtualFrame frame, Object callee, Object[] arguments, @SuppressWarnings("unused") PKeyword[] keywords,
- @SuppressWarnings("unused") @Bind("this") Node inliningTarget,
- @Cached("createFor(this)") IndirectCallData indirectCallData,
- @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode,
- @CachedLibrary(limit = "4") InteropLibrary lib,
- @Cached PForeignToPTypeNode toPTypeNode,
- @Cached GilNode gil,
- @Cached PRaiseNode.Lazy raiseNode) {
- PythonLanguage language = PythonLanguage.get(inliningTarget);
- PythonContext context = PythonContext.get(inliningTarget);
- try {
- Object state = IndirectCallContext.enter(frame, language, context, indirectCallData);
- gil.release(true);
- try {
- if (lib.isExecutable(callee)) {
- return toPTypeNode.executeConvert(lib.execute(callee, arguments));
- } else {
- return toPTypeNode.executeConvert(lib.instantiate(callee, arguments));
- }
- } finally {
- gil.acquire();
- IndirectCallContext.exit(frame, language, context, state);
- }
- } catch (ArityException | UnsupportedTypeException | UnsupportedMessageException e) {
- throw raiseNode.get(inliningTarget).raise(PythonErrorType.TypeError, ErrorMessages.INVALID_INSTANTIATION_OF_FOREIGN_OBJ);
- }
- }
-
- @Fallback
- @SuppressWarnings("unused")
- static Object doGeneric(Object callee, Object arguments, Object keywords,
- @Cached PRaiseNode raiseNode) {
- throw raiseNode.raise(PythonErrorType.TypeError, ErrorMessages.INVALID_INSTANTIATION_OF_FOREIGN_OBJ);
- }
-
- @NeverDefault
- public static CallNode create() {
- return ForeignObjectBuiltinsFactory.CallNodeFactory.create(null);
- }
- }
-
@Slot(value = SlotKind.tp_getattro, isComplex = true)
@GenerateNodeFactory
abstract static class GetAttributeNode extends GetAttrBuiltinNode {
@@ -497,47 +376,4 @@ protected TruffleString defaultConversion(VirtualFrame frame, InteropLibrary lib
}
}
- @Builtin(name = J___BASES__, minNumOfPositionalArgs = 1, isGetter = true, isSetter = false)
- @GenerateNodeFactory
- @ImportStatic(PGuards.class)
- abstract static class BasesNode extends PythonUnaryBuiltinNode {
- @Specialization(limit = "3")
- static Object getBases(Object self,
- @Bind("this") Node inliningTarget,
- @CachedLibrary("self") InteropLibrary lib,
- @Cached PythonObjectFactory factory,
- @Cached PRaiseNode.Lazy raiseNode) {
- if (lib.isMetaObject(self)) {
- return factory.createTuple(PythonUtils.EMPTY_OBJECT_ARRAY);
- } else {
- throw raiseNode.get(inliningTarget).raise(AttributeError, ErrorMessages.FOREIGN_OBJ_HAS_NO_ATTR_S, T___BASES__);
- }
- }
- }
-
- @Builtin(name = J___INSTANCECHECK__, minNumOfPositionalArgs = 2)
- @GenerateNodeFactory
- @ImportStatic(PGuards.class)
- abstract static class InstancecheckNode extends PythonBinaryBuiltinNode {
- @Specialization(limit = "3")
- static Object check(Object self, Object instance,
- @Bind("this") Node inliningTarget,
- @CachedLibrary("self") InteropLibrary lib,
- @Cached GilNode gil,
- @Cached PRaiseNode.Lazy raiseNode) {
- if (lib.isMetaObject(self)) {
- gil.release(true);
- try {
- return lib.isMetaInstance(self, instance);
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere();
- } finally {
- gil.acquire();
- }
- } else {
- throw raiseNode.get(inliningTarget).raise(AttributeError, ErrorMessages.FOREIGN_OBJ_HAS_NO_ATTR_S, T___INSTANCECHECK__);
- }
- }
- }
-
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/TeeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/TeeBuiltins.java
index 29fad45e82..69040f00c5 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/TeeBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/TeeBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2025, 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
@@ -78,7 +78,6 @@
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NeverDefault;
@@ -144,27 +143,21 @@ static Object iter(PTee self) {
@Builtin(name = J___NEXT__, minNumOfPositionalArgs = 1)
@GenerateNodeFactory
public abstract static class NextNode extends PythonUnaryBuiltinNode {
- @Specialization(guards = "self.getIndex() < LINKCELLS")
- static Object next(VirtualFrame frame, PTee self,
+ @Specialization
+ static Object doIt(VirtualFrame frame, PTee self,
@Bind("this") Node inliningTarget,
- @Shared @Cached BuiltinFunctions.NextNode nextNode,
- @Shared @Cached PRaiseNode.Lazy raiseNode) {
+ @Cached PythonObjectFactory factory,
+ @Cached InlinedConditionProfile indexConditionProfile,
+ @Cached BuiltinFunctions.NextNode nextNode,
+ @Cached PRaiseNode.Lazy raiseNode) {
+ if (indexConditionProfile.profile(inliningTarget, self.getIndex() >= LINKCELLS)) {
+ self.setDataObj(self.getDataobj().jumplink(factory));
+ self.setIndex(0);
+ }
Object value = self.getDataobj().getItem(frame, inliningTarget, self.getIndex(), nextNode, raiseNode);
self.setIndex(self.getIndex() + 1);
return value;
}
-
- @Specialization(guards = "self.getIndex() >= LINKCELLS")
- static Object nextNext(VirtualFrame frame, PTee self,
- @Bind("this") Node inliningTarget,
- @Shared @Cached BuiltinFunctions.NextNode nextNode,
- @Cached PythonObjectFactory factory,
- @Shared @Cached PRaiseNode.Lazy raiseNode) {
- self.setDataObj(self.getDataobj().jumplink(factory));
- Object value = self.getDataobj().getItem(frame, inliningTarget, 0, nextNode, raiseNode);
- self.setIndex(1);
- return value;
- }
}
@Builtin(name = J___REDUCE__, minNumOfPositionalArgs = 1)
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/FrozenModules.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/FrozenModules.java
index ad7000fa5e..bef91e264c 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/FrozenModules.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/FrozenModules.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2025, 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
@@ -218,8 +218,10 @@ private static final class Map {
private static final PythonFrozenModule FROZEN_ONLY = new PythonFrozenModule("FROZEN_ONLY", null, false);
private static final PythonFrozenModule POLYGLOT_ARROW = new PythonFrozenModule("POLYGLOT_ARROW", null, false);
private static final PythonFrozenModule _SYSCONFIGDATA = new PythonFrozenModule("_SYSCONFIGDATA", null, false);
+ private static final PythonFrozenModule _POLYGLOT = new PythonFrozenModule("_POLYGLOT", null, false);
+ private static final PythonFrozenModule _POLYGLOT_DATETIME = new PythonFrozenModule("_POLYGLOT_DATETIME", null, false);
+ private static final PythonFrozenModule _POLYGLOT_TIME = new PythonFrozenModule("_POLYGLOT_TIME", null, false);
private static final PythonFrozenModule GRAALPY___GRAALPYTHON__ = new PythonFrozenModule("GRAALPY___GRAALPYTHON__", null, false);
- private static final PythonFrozenModule GRAALPY__POLYGLOT = new PythonFrozenModule("GRAALPY__POLYGLOT", null, false);
private static final PythonFrozenModule GRAALPY__SRE = new PythonFrozenModule("GRAALPY__SRE", null, false);
private static final PythonFrozenModule GRAALPY__SYSCONFIG = new PythonFrozenModule("GRAALPY__SYSCONFIG", null, false);
private static final PythonFrozenModule GRAALPY__WEAKREF = new PythonFrozenModule("GRAALPY__WEAKREF", null, false);
@@ -227,6 +229,7 @@ private static final class Map {
private static final PythonFrozenModule GRAALPY_JAVA = new PythonFrozenModule("GRAALPY_JAVA", null, false);
private static final PythonFrozenModule GRAALPY_PIP_HOOK = new PythonFrozenModule("GRAALPY_PIP_HOOK", null, false);
private static final PythonFrozenModule GRAALPY_UNICODEDATA = new PythonFrozenModule("GRAALPY_UNICODEDATA", null, false);
+ private static final PythonFrozenModule GRAALPY__NT = new PythonFrozenModule("GRAALPY__NT", null, false);
}
public static final PythonFrozenModule lookup(String name) {
@@ -595,10 +598,14 @@ public static final PythonFrozenModule lookup(String name) {
return Map.POLYGLOT_ARROW;
case "_sysconfigdata":
return Map._SYSCONFIGDATA;
+ case "_polyglot":
+ return Map._POLYGLOT;
+ case "_polyglot_datetime":
+ return Map._POLYGLOT_DATETIME;
+ case "_polyglot_time":
+ return Map._POLYGLOT_TIME;
case "graalpy.__graalpython__":
return Map.GRAALPY___GRAALPYTHON__;
- case "graalpy._polyglot":
- return Map.GRAALPY__POLYGLOT;
case "graalpy._sre":
return Map.GRAALPY__SRE;
case "graalpy._sysconfig":
@@ -613,6 +620,8 @@ public static final PythonFrozenModule lookup(String name) {
return Map.GRAALPY_PIP_HOOK;
case "graalpy.unicodedata":
return Map.GRAALPY_UNICODEDATA;
+ case "graalpy._nt":
+ return Map.GRAALPY__NT;
default:
return null;
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TpSlots.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TpSlots.java
index 497d8f0470..0685be32b0 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TpSlots.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TpSlots.java
@@ -842,7 +842,7 @@ public static TpSlots fromNative(PythonAbstractNativeObject pythonClass, PythonC
existingSlotWrapper = execWrapper;
} else if (executable != null) {
// This can happen for legacy slots where the delegate would be a PFunction
- LOGGER.warning(() -> String.format("Unexpected executable for slot pointer: %s", executable));
+ LOGGER.fine(() -> String.format("Unexpected executable for slot pointer: %s", executable));
}
} catch (UnsupportedMessageException e) {
throw new IllegalStateException(e);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java
index f15c1e4196..37dbb36db7 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java
@@ -334,7 +334,6 @@ public abstract class ErrorMessages {
public static final TruffleString FAILED_TO_CONVERT_SEQ = tsLiteral("failed to convert sequence");
public static final TruffleString FLOAT_ARG_REQUIRED = tsLiteral("float argument required, not %p");
public static final TruffleString FOREIGN_OBJ_HAS_NO_ATTR_S = tsLiteral("foreign object has no attribute '%s'");
- public static final TruffleString FOREIGN_OBJ_ISNT_ITERABLE = tsLiteral("foreign object is not iterable");
public static final TruffleString FOREIGN_OBJ_ISNT_REVERSE_ITERABLE = tsLiteral("foreign object cannot be iterated in reverse");
public static final TruffleString FORMAT_REQUIRES_MAPPING = tsLiteral("format requires a mapping");
public static final TruffleString FORMAT_STR_CONTAINS_POS_FIELDS = tsLiteral("Format string contains positional fields");
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java
index 012300b2b4..6ce3f2f5c1 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2025, 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
@@ -50,6 +50,7 @@
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
+import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageSetItem;
import com.oracle.graal.python.builtins.objects.dict.PDict;
import com.oracle.graal.python.builtins.objects.object.PythonObject;
@@ -188,7 +189,7 @@ private static boolean writeToDynamicStorageManagedClass(PythonManagedClass klas
}
// write to the dict: the basic specialization for non-classes
- @Specialization(guards = {"dict != null", "!isManagedClass(object)"})
+ @Specialization(guards = {"dict != null", "!isManagedClass(object)", "!isNoValue(value)"})
static boolean writeToDictNoType(@SuppressWarnings("unused") PythonObject object, TruffleString key, Object value,
@Bind("this") Node inliningTarget,
@SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
@@ -199,7 +200,7 @@ static boolean writeToDictNoType(@SuppressWarnings("unused") PythonObject object
}
// write to the dict & PythonManagedClass -> requires calling onAttributeUpdate
- @Specialization(guards = {"dict != null"})
+ @Specialization(guards = {"dict != null", "!isNoValue(value)"})
boolean writeToDictBuiltinType(PythonBuiltinClass klass, TruffleString key, Object value,
@Bind("this") Node inliningTarget,
@SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
@@ -216,7 +217,7 @@ boolean writeToDictBuiltinType(PythonBuiltinClass klass, TruffleString key, Obje
}
}
- @Specialization(guards = {"dict != null"})
+ @Specialization(guards = {"dict != null", "!isNoValue(value)"})
static boolean writeToDictClass(PythonClass klass, TruffleString key, Object value,
@Bind("this") Node inliningTarget,
@SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
@@ -229,6 +230,36 @@ static boolean writeToDictClass(PythonClass klass, TruffleString key, Object val
return writeToDictManagedClass(klass, dict, key, value, inliningTarget, callAttrUpdate, updateStorage, setHashingStorageItem, codePointLengthNode, codePointAtIndexNode);
}
+ @Specialization(guards = {"dict != null", "isNoValue(value)", "!isPythonBuiltinClass(obj)"})
+ static boolean deleteFromPythonObject(PythonObject obj, TruffleString key, Object value,
+ @Bind("this") Node inliningTarget,
+ @SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
+ @Bind("getDict.execute(obj)") PDict dict,
+ @Shared("callAttrUpdate") @Cached InlinedBranchProfile callAttrUpdate,
+ @Cached HashingStorageNodes.HashingStorageDelItem hashingStorageDelItem,
+ @Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode,
+ @Shared("cpAtIndex") @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode) {
+ try {
+ HashingStorage dictStorage = dict.getDictStorage();
+ return hashingStorageDelItem.execute(inliningTarget, dictStorage, key, dict);
+ } finally {
+ if (obj instanceof PythonManagedClass klass) {
+ if (!klass.canSkipOnAttributeUpdate(key, value, codePointLengthNode, codePointAtIndexNode)) {
+ callAttrUpdate.enter(inliningTarget);
+ klass.onAttributeUpdate(key, value);
+ }
+ }
+ }
+ }
+
+ @Specialization(guards = {"dict != null", "isNoValue(value)"})
+ static boolean deleteFromPythonBuiltinClass(PythonBuiltinClass klass, TruffleString key, Object value,
+ @Bind("this") Node inliningTarget,
+ @SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
+ @Bind("getDict.execute(klass)") PDict dict) {
+ throw PRaiseNode.raiseUncached(inliningTarget, TypeError, ErrorMessages.CANT_SET_ATTRIBUTE_R_OF_IMMUTABLE_TYPE_N, key, klass);
+ }
+
private static boolean writeToDictManagedClass(PythonManagedClass klass, PDict dict, TruffleString key, Object value, Node inliningTarget,
InlinedBranchProfile callAttrUpdate, InlinedBranchProfile updateStorage, HashingStorageSetItem setHashingStorageItem, TruffleString.CodePointLengthNode codePointLengthNode,
TruffleString.CodePointAtIndexNode codePointAtIndexNode) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java
index f20a4ea9cd..53f1fe05d0 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java
@@ -195,12 +195,7 @@ private AbstractTruffleException handlePythonException(AbstractTruffleException
if (PythonOptions.isPExceptionWithJavaStacktrace(getPythonLanguage()) && e instanceof PException pe) {
ExceptionUtils.printJavaStackTrace(pe);
}
- if (!getSourceSection().getSource().isInteractive()) {
- if (getContext().isChildContext()) {
- getContext().getChildContextData().setExitCode(1);
- }
- throw new PythonExitException(this, 1);
- }
+ exit(1);
}
// Before we leave Python, format the message since outside the context
if (e instanceof PException pe) {
@@ -209,6 +204,15 @@ private AbstractTruffleException handlePythonException(AbstractTruffleException
throw e;
}
+ private void exit(int exitCode) {
+ if (!getSourceSection().getSource().isInteractive()) {
+ if (getContext().isChildContext()) {
+ getContext().getChildContextData().setExitCode(1);
+ }
+ throw new PythonExitException(this, exitCode);
+ }
+ }
+
private static boolean isSystemExit(PBaseException pythonException) {
return IsBuiltinClassProfile.profileClassSlowPath(GetPythonObjectClassNode.executeUncached(pythonException), SystemExit);
}
@@ -227,6 +231,7 @@ private void handleJavaException(Throwable e) {
if (PythonOptions.shouldPrintJavaStacktrace(getPythonLanguage(), e)) {
e.printStackTrace();
}
+ exit(1);
}
} catch (UnsupportedMessageException unsupportedMessageException) {
throw CompilerDirectives.shouldNotReachHere();
@@ -250,12 +255,12 @@ private void handleSystemExit(PBaseException pythonException) {
int exitcode = getExitCode(pythonException);
throw new PythonExitException(this, exitcode);
} catch (CannotCastException e) {
- // fall through
- }
- if (handleAlwaysRunExceptHook(theContext, pythonException)) {
- throw new PythonExitException(this, 1);
+ if (handleAlwaysRunExceptHook(theContext, pythonException)) {
+ throw new PythonExitException(this, 1);
+ } else {
+ throw pythonException.getExceptionForReraise(pythonException.getTraceback());
+ }
}
- throw pythonException.getExceptionForReraise(pythonException.getTraceback());
}
@TruffleBoundary
@@ -264,12 +269,12 @@ private Object handleChildContextExit(PBaseException pythonException) throws PEx
try {
return getExitCode(pythonException);
} catch (CannotCastException cce) {
- // fall through
- }
- if (handleAlwaysRunExceptHook(getContext(), pythonException)) {
- return 1;
+ if (handleAlwaysRunExceptHook(getContext(), pythonException)) {
+ return 1;
+ } else {
+ throw pythonException.getExceptionForReraise(pythonException.getTraceback());
+ }
}
- throw pythonException.getExceptionForReraise(pythonException.getTraceback());
}
private static int getExitCode(PBaseException pythonException) throws CannotCastException {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetForeignObjectClassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetForeignObjectClassNode.java
index b40c4afbcf..b7b041b290 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetForeignObjectClassNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetForeignObjectClassNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, 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
@@ -87,21 +87,26 @@ public enum Trait {
// The type field is only set for cases which are already implemented.
// First in MRO
+ // Interop types first as they are the most concrete/specific types
+ NULL("None", PythonBuiltinClassType.PNone),
BOOLEAN("Boolean", PythonBuiltinClassType.ForeignBoolean),
NUMBER("Number", PythonBuiltinClassType.ForeignNumber), // int, float, complex
STRING("String", PythonBuiltinClassType.PString),
+ EXCEPTION("Exception", PythonBuiltinClassType.PBaseException),
+ META_OBJECT("AbstractClass", PythonBuiltinClassType.ForeignAbstractClass),
+
+ // Interop traits
+ EXECUTABLE("Executable", PythonBuiltinClassType.ForeignExecutable),
+ INSTANTIABLE("Instantiable", PythonBuiltinClassType.ForeignInstantiable),
+
+ // Container traits/types must be last, see comment above
// Hash before Array so that foreign dict+list prefers dict.[]
HASH("Dict", PythonBuiltinClassType.PDict),
// Array before Iterable so that foreign list+iterable prefers list.__iter__
ARRAY("List", PythonBuiltinClassType.PList),
- EXCEPTION("Exception", PythonBuiltinClassType.PBaseException),
- EXECUTABLE("Executable"),
- INSTANTIABLE("Instantiable"),
// Iterator before Iterable so that foreign iterator+iterable prefers iterator.__iter__
ITERATOR("Iterator", PythonBuiltinClassType.PIterator),
- ITERABLE("Iterable"),
- META_OBJECT("AbstractClass"), // PythonBuiltinClassType.PythonClass ?
- NULL("None", PythonBuiltinClassType.PNone);
+ ITERABLE("Iterable", PythonBuiltinClassType.ForeignIterable);
// Last in MRO
public static final Trait[] VALUES = Trait.values();
@@ -111,10 +116,6 @@ public enum Trait {
final int bit;
final PythonBuiltinClassType type;
- Trait(String name) {
- this(name, null);
- }
-
Trait(String name, PythonBuiltinClassType type) {
this.name = name;
this.bit = 1 << ordinal();
@@ -217,14 +218,14 @@ private PythonManagedClass resolvePolyglotForeignClass(int traits) {
traitsList.add(classForTraits(trait.bit));
}
- if (trait == Trait.INSTANTIABLE && Trait.META_OBJECT.isSet(traits)) {
- // Deal with it when we are at trait META_OBJECT
- } else if (trait == Trait.META_OBJECT) {
+ if (trait == Trait.META_OBJECT) {
if (Trait.INSTANTIABLE.isSet(traits)) {
nameBuilder.append("Class");
} else {
nameBuilder.append("AbstractClass");
}
+ } else if (trait == Trait.INSTANTIABLE && Trait.META_OBJECT.isSet(traits)) {
+ // Dealt with above
} else {
nameBuilder.append(trait.name);
}
diff --git a/graalpython/graalpy-archetype-polyglot-app/pom.xml b/graalpython/graalpy-archetype-polyglot-app/pom.xml
index d08b5631bb..e525172eb1 100644
--- a/graalpython/graalpy-archetype-polyglot-app/pom.xml
+++ b/graalpython/graalpy-archetype-polyglot-app/pom.xml
@@ -45,7 +45,7 @@ SOFTWARE.
org.graalvm.python
graalpy-archetype-polyglot-app
- 25.0.0
+ 24.2.1
http://www.graalvm.org/python
Maven archetype providing a skeleton GraalPy - Java polyglot application.
maven-archetype
diff --git a/graalpython/graalpy-archetype-polyglot-app/src/main/resources/archetype-resources/pom.xml b/graalpython/graalpy-archetype-polyglot-app/src/main/resources/archetype-resources/pom.xml
index eb03184b9f..e67d93e879 100644
--- a/graalpython/graalpy-archetype-polyglot-app/src/main/resources/archetype-resources/pom.xml
+++ b/graalpython/graalpy-archetype-polyglot-app/src/main/resources/archetype-resources/pom.xml
@@ -10,7 +10,7 @@
#set( $symbol_dollar = '$' )
- 25.0.0
+ 24.2.1
python-community
0.10.4
17
diff --git a/graalpython/graalpy-jbang/examples/hello.java b/graalpython/graalpy-jbang/examples/hello.java
index 2afc0c82b3..11049392fc 100644
--- a/graalpython/graalpy-jbang/examples/hello.java
+++ b/graalpython/graalpy-jbang/examples/hello.java
@@ -40,7 +40,7 @@
*/
///usr/bin/env jbang "$0" "$@" ; exit $?
//JAVA 17+
-//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:25.0.0}
+//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:24.2.1}
// specify python packages and their versions as if used with pip
//PIP termcolor==2.2
diff --git a/graalpython/graalpy-jbang/templates/graalpy-template.java.qute b/graalpython/graalpy-jbang/templates/graalpy-template.java.qute
index 5258d8cb30..9a94477922 100644
--- a/graalpython/graalpy-jbang/templates/graalpy-template.java.qute
+++ b/graalpython/graalpy-jbang/templates/graalpy-template.java.qute
@@ -5,7 +5,7 @@
{/for}
{#if dependencies.isEmpty()}// //DEPS {/if}
{|
-//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:25.0.0}
+//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:24.2.1}
// specify python packages and their versions as if used with pip
//PIP termcolor==2.2
|}
diff --git a/graalpython/graalpy-jbang/templates/graalpy-template_local_repo.java.qute b/graalpython/graalpy-jbang/templates/graalpy-template_local_repo.java.qute
index 9db2304494..6b41f4e288 100644
--- a/graalpython/graalpy-jbang/templates/graalpy-template_local_repo.java.qute
+++ b/graalpython/graalpy-jbang/templates/graalpy-template_local_repo.java.qute
@@ -8,7 +8,7 @@
//REPOS mc=https://repo1.maven.org/maven2/
//REPOS local=file://{path_to_local_repo}
{|
-//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:25.0.0}
+//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:24.2.1}
// specify python packages and their versions as if used with pip
//PIP termcolor==2.2
|}
diff --git a/graalpython/graalpy-maven-plugin/pom.xml b/graalpython/graalpy-maven-plugin/pom.xml
index 7a8715415f..5a8fe1acde 100644
--- a/graalpython/graalpy-maven-plugin/pom.xml
+++ b/graalpython/graalpy-maven-plugin/pom.xml
@@ -48,7 +48,7 @@ SOFTWARE.
graalpy-maven-plugin
maven-plugin
- 25.0.0
+ 24.2.1
http://www.graalvm.org/python
graalpy-maven-plugin
Handles python related resources in a maven GraalPy - Java polyglot application.
@@ -57,7 +57,7 @@ SOFTWARE.
17
17
UTF-8
- 25.0.0
+ 24.2.1
diff --git a/graalpython/graalpy-maven-plugin/src/main/java/org/graalvm/python/maven/plugin/ManageResourcesMojo.java b/graalpython/graalpy-maven-plugin/src/main/java/org/graalvm/python/maven/plugin/ManageResourcesMojo.java
index bc2b69ceae..d81b78b861 100644
--- a/graalpython/graalpy-maven-plugin/src/main/java/org/graalvm/python/maven/plugin/ManageResourcesMojo.java
+++ b/graalpython/graalpy-maven-plugin/src/main/java/org/graalvm/python/maven/plugin/ManageResourcesMojo.java
@@ -145,7 +145,7 @@ public void execute() throws MojoExecutionException {
}
if (resourceDirectory == null) {
- if (externalDirectory != null) {
+ if (externalDirectory == null) {
getLog().info(String.format("Virtual filesystem is deployed to default resources directory '%s'. " +
"This can cause conflicts if used with other Java libraries that also deploy GraalPy virtual filesystem. " +
"Consider adding GRAALPY-VFS/${project.groupId}/${project.artifactId} to your pom.xml, " +
@@ -162,7 +162,7 @@ public void execute() throws MojoExecutionException {
getLog().warn("The GraalPy plugin configuration setting was deprecated and has no effect anymore.\n" +
"For execution in jvm mode, the python language home is always available.\n" +
"When building a native executable using GraalVM Native Image, then the full python language home is by default embedded into the native executable.\n" +
- "For more details, please refer to the documentation of GraalVM Native Image options IncludeLanguageResources and CopyLanguageResources documentation.");
+ "For more details, please refer to the documentation of GraalVM Native Image options IncludeLanguageResources and CopyLanguageResources.");
}
manageVenv();
diff --git a/graalpython/lib-graalpython/_nt.py b/graalpython/lib-graalpython/_nt.py
new file mode 100644
index 0000000000..4fbd2a2591
--- /dev/null
+++ b/graalpython/lib-graalpython/_nt.py
@@ -0,0 +1,54 @@
+# Copyright (c) 2025, 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import nt
+
+
+def _add_dll_directory(path):
+ import ctypes
+ return ctypes.windll.kernel32.AddDllDirectory(path)
+
+
+def _remove_dll_directory(cookie):
+ import ctypes
+ return ctypes.windll.kernel32.RemoveDllDirectory(cookie)
+
+
+nt._add_dll_directory = _add_dll_directory
+nt._remove_dll_directory = _remove_dll_directory
\ No newline at end of file
diff --git a/graalpython/lib-graalpython/_polyglot.py b/graalpython/lib-graalpython/modules/_polyglot.py
similarity index 67%
rename from graalpython/lib-graalpython/_polyglot.py
rename to graalpython/lib-graalpython/modules/_polyglot.py
index caf9cebdc0..770575c8a8 100644
--- a/graalpython/lib-graalpython/_polyglot.py
+++ b/graalpython/lib-graalpython/modules/_polyglot.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2023, 2025, 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
@@ -37,9 +37,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
-import datetime
import polyglot
-import time
def interop_type(foreign_class, allow_method_overwrites=False):
@@ -112,37 +110,6 @@ def wrapper(python_class):
setattr(polyglot, "interop_behavior", interop_behavior)
-def _date_time_tz(dt: datetime.datetime):
- if dt.tzinfo is not None:
- utcoffset = dt.tzinfo.utcoffset(dt)
- return utcoffset.days * 3600 * 24 + utcoffset.seconds
- raise polyglot.UnsupportedMessage
-
-
-def _struct_time_tz(st: time.struct_time):
- if st.tm_gmtoff is not None:
- return st.tm_gmtoff
- return st.tm_zone
-
-
-polyglot.register_interop_behavior(datetime.time,
- is_time=True, as_time=lambda d: (d.hour, d.minute, d.second, d.microsecond),
- is_time_zone=lambda t: t.tzinfo is not None, as_time_zone=_date_time_tz)
-
-polyglot.register_interop_behavior(datetime.date,
- is_date=True, as_date=lambda d: (d.year, d.month, d.day))
-
-polyglot.register_interop_behavior(datetime.datetime,
- is_date=True, as_date=lambda d: (d.year, d.month, d.day),
- is_time=True, as_time=lambda d: (d.hour, d.minute, d.second, d.microsecond),
- is_time_zone=lambda t: t.tzinfo is not None, as_time_zone=_date_time_tz)
-
-polyglot.register_interop_behavior(time.struct_time,
- is_date=True, as_date=lambda t: (t.tm_year, t.tm_mon, t.tm_mday),
- is_time=True, as_time=lambda t: (t.tm_hour, t.tm_min, t.tm_sec, 0),
- is_time_zone=lambda t: t.tm_zone is not None or t.tm_gmtoff is not None,
- as_time_zone=_struct_time_tz)
-
# loading arrow structures on demand
def __getattr__(name):
if name == "arrow":
@@ -153,33 +120,3 @@ def __getattr__(name):
setattr(polyglot, "__getattr__", __getattr__)
setattr(polyglot, "__path__", ".")
-
-# example extending time.struct_time using the decorator wrapper
-#
-# @polyglot.interop_behavior(time.struct_time)
-# class StructTimeInteropBehavior:
-# @staticmethod
-# def is_date(t):
-# return True
-#
-# @staticmethod
-# def as_date(t):
-# return t.tm_year, t.tm_mon, t.tm_mday
-#
-# @staticmethod
-# def is_time(t):
-# return True
-#
-# @staticmethod
-# def as_time(t):
-# return t.tm_hour, t.tm_min, t.tm_sec, 0
-#
-# @staticmethod
-# def is_time_zone(t):
-# return t.tm_zone is not None or t.tm_gmtoff is not None
-#
-# @staticmethod
-# def as_time_zone(t):
-# if t.tm_gmtoff is not None:
-# return t.tm_gmtoff
-# return t.tm_zone
diff --git a/graalpython/lib-graalpython/modules/_polyglot_datetime.py b/graalpython/lib-graalpython/modules/_polyglot_datetime.py
new file mode 100644
index 0000000000..80f73a74de
--- /dev/null
+++ b/graalpython/lib-graalpython/modules/_polyglot_datetime.py
@@ -0,0 +1,60 @@
+# Copyright (c) 2023, 2025, 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import datetime
+import polyglot
+
+def _date_time_tz(dt: datetime.datetime):
+ if dt.tzinfo is not None:
+ utcoffset = dt.tzinfo.utcoffset(dt)
+ return utcoffset.days * 3600 * 24 + utcoffset.seconds
+ raise polyglot.UnsupportedMessage
+
+
+polyglot.register_interop_behavior(datetime.time,
+ is_time=True, as_time=lambda d: (d.hour, d.minute, d.second, d.microsecond),
+ is_time_zone=lambda t: t.tzinfo is not None, as_time_zone=_date_time_tz)
+
+polyglot.register_interop_behavior(datetime.date,
+ is_date=True, as_date=lambda d: (d.year, d.month, d.day))
+
+polyglot.register_interop_behavior(datetime.datetime,
+ is_date=True, as_date=lambda d: (d.year, d.month, d.day),
+ is_time=True, as_time=lambda d: (d.hour, d.minute, d.second, d.microsecond),
+ is_time_zone=lambda t: t.tzinfo is not None, as_time_zone=_date_time_tz)
diff --git a/graalpython/lib-graalpython/modules/_polyglot_time.py b/graalpython/lib-graalpython/modules/_polyglot_time.py
new file mode 100644
index 0000000000..ed4dc0fc54
--- /dev/null
+++ b/graalpython/lib-graalpython/modules/_polyglot_time.py
@@ -0,0 +1,84 @@
+# Copyright (c) 2023, 2025, 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import time
+import polyglot
+
+
+def _struct_time_tz(st: time.struct_time):
+ if st.tm_gmtoff is not None:
+ return st.tm_gmtoff
+ return st.tm_zone
+
+
+polyglot.register_interop_behavior(time.struct_time,
+ is_date=True, as_date=lambda t: (t.tm_year, t.tm_mon, t.tm_mday),
+ is_time=True, as_time=lambda t: (t.tm_hour, t.tm_min, t.tm_sec, 0),
+ is_time_zone=lambda t: t.tm_zone is not None or t.tm_gmtoff is not None,
+ as_time_zone=_struct_time_tz)
+
+# example extending time.struct_time using the decorator wrapper
+#
+# @polyglot.interop_behavior(time.struct_time)
+# class StructTimeInteropBehavior:
+# @staticmethod
+# def is_date(t):
+# return True
+#
+# @staticmethod
+# def as_date(t):
+# return t.tm_year, t.tm_mon, t.tm_mday
+#
+# @staticmethod
+# def is_time(t):
+# return True
+#
+# @staticmethod
+# def as_time(t):
+# return t.tm_hour, t.tm_min, t.tm_sec, 0
+#
+# @staticmethod
+# def is_time_zone(t):
+# return t.tm_zone is not None or t.tm_gmtoff is not None
+#
+# @staticmethod
+# def as_time_zone(t):
+# if t.tm_gmtoff is not None:
+# return t.tm_gmtoff
+# return t.tm_zone
diff --git a/graalpython/lib-graalpython/patches/metadata.toml b/graalpython/lib-graalpython/patches/metadata.toml
index ac1ccb7262..855e1f8831 100644
--- a/graalpython/lib-graalpython/patches/metadata.toml
+++ b/graalpython/lib-graalpython/patches/metadata.toml
@@ -337,6 +337,11 @@ version = '== 3.10.5'
patch = 'orjson-3.10.5.patch'
license = 'Apache-2.0 OR MIT'
+[[ormsgpack.rules]]
+version = '>= 1.8.0, <= 1.9.1'
+patch = 'ormsgpack-1.8.0-1.9.1.patch'
+license = 'Apache-2.0 OR MIT'
+
[[overrides.rules]]
version = '== 7.4.0'
# Important: This patch esentially breaks the package, it's not upstreamable. The package relies on bytecode parsing
@@ -476,6 +481,15 @@ version = ">= 4.8.0"
patch = 'pymongo-4.8.0.patch'
license = 'Apache-2.0'
+[[PyMuPDF.rules]]
+version = "== 1.25.4"
+patch = "pymupdf.patch"
+# That project is AGPL, so do not actually include *any* code of pymupdf in the patch, not even an
+# empty line, in the diff context. The code we write in the patch is UPL - that is compatible with
+# AGPL in the sense that if someone were to apply it and distribute *that*, our patch is now part
+# of the AGPL'd codebase
+license = 'UPL'
+
[[pyOpenSSL.rules]]
# Pin this version to avoid pulling newer cryptography than we have patch for
version = "== 23.2.0"
diff --git a/graalpython/lib-graalpython/patches/ormsgpack-1.8.0-1.9.1.patch b/graalpython/lib-graalpython/patches/ormsgpack-1.8.0-1.9.1.patch
new file mode 100644
index 0000000000..3a9542a058
--- /dev/null
+++ b/graalpython/lib-graalpython/patches/ormsgpack-1.8.0-1.9.1.patch
@@ -0,0 +1,364 @@
+diff --git a/src/deserialize/deserializer.rs b/src/deserialize/deserializer.rs
+index 41cf7f1..99cd68e 100644
+--- a/src/deserialize/deserializer.rs
++++ b/src/deserialize/deserializer.rs
+@@ -292,7 +292,10 @@ impl<'de> Deserializer<'de> {
+ marker => Err(Error::InvalidType(marker)),
+ }?;
+ let value = self.deserialize()?;
++ #[cfg(not(GraalPy))]
+ let pyhash = unsafe { (*key.as_ptr().cast::()).hash };
++ #[cfg(GraalPy)]
++ let pyhash = unsafe { pyo3::ffi::PyObject_Hash(key.as_ptr()) };
+ let _ = ffi!(_PyDict_SetItem_KnownHash(
+ dict_ptr,
+ key.as_ptr(),
+@@ -471,7 +474,7 @@ impl<'de> Deserializer<'de> {
+ let ptr = ffi!(PyTuple_New(len as pyo3::ffi::Py_ssize_t));
+ for i in 0..len {
+ let elem = self.deserialize_map_key()?;
+- ffi!(PyTuple_SET_ITEM(
++ ffi!(PyTuple_SetItem(
+ ptr,
+ i as pyo3::ffi::Py_ssize_t,
+ elem.as_ptr()
+diff --git a/src/ext.rs b/src/ext.rs
+index b2573b4..9668d4f 100644
+--- a/src/ext.rs
++++ b/src/ext.rs
+@@ -22,7 +22,7 @@ unsafe extern "C" fn ext_new(
+ );
+ return null_mut();
+ }
+- let tag = PyTuple_GET_ITEM(args, 0);
++ let tag = PyTuple_GetItem(args, 0);
+ if PyLong_Check(tag) == 0 {
+ PyErr_SetString(
+ PyExc_TypeError,
+@@ -30,7 +30,7 @@ unsafe extern "C" fn ext_new(
+ );
+ return null_mut();
+ }
+- let data = PyTuple_GET_ITEM(args, 1);
++ let data = PyTuple_GetItem(args, 1);
+ if PyBytes_Check(data) == 0 {
+ PyErr_SetString(
+ PyExc_TypeError,
+diff --git a/src/ffi.rs b/src/ffi.rs
+index 4e5ddc3..20c9db4 100644
+--- a/src/ffi.rs
++++ b/src/ffi.rs
+@@ -7,13 +7,16 @@ use std::ptr::NonNull;
+ #[allow(non_snake_case)]
+ #[inline(always)]
+ pub unsafe fn PyBytes_AS_STRING(op: *mut PyObject) -> *const c_char {
+- &(*op.cast::()).ob_sval as *const c_char
++ #[cfg(not(any(PyPy, GraalPy, Py_LIMITED_API)))]
++ return &(*op.cast::()).ob_sval as *const c_char;
++ #[cfg(any(PyPy, GraalPy, Py_LIMITED_API))]
++ return crate::PyBytes_AsString(op);
+ }
+
+ #[allow(non_snake_case)]
+ #[inline(always)]
+ pub unsafe fn PyBytes_GET_SIZE(op: *mut PyObject) -> Py_ssize_t {
+- (*op.cast::()).ob_size
++ Py_SIZE(op)
+ }
+
+ #[repr(C)]
+@@ -63,11 +66,21 @@ pub fn pylong_is_positive(op: *mut PyObject) -> bool {
+ unsafe { (*(op as *mut PyLongObject)).long_value.lv_tag & SIGN_MASK == 0 }
+ }
+
+-#[cfg(not(Py_3_12))]
++#[cfg(not(any(Py_3_12, GraalPy)))]
+ pub fn pylong_is_positive(op: *mut PyObject) -> bool {
+ unsafe { (*(op as *mut PyVarObject)).ob_size > 0 }
+ }
+
++extern "C" {
++ #[cfg(not(PyPy))]
++ pub fn _PyLong_Sign(v: *mut PyObject) -> c_int;
++}
++
++#[cfg(GraalPy)]
++pub fn pylong_is_positive(op: *mut PyObject) -> bool {
++ unsafe { _PyLong_Sign(op) > 0 }
++}
++
+ pub struct PyDictIter {
+ op: *mut PyObject,
+ pos: isize,
+diff --git a/src/lib.rs b/src/lib.rs
+index f10b1c4..1a9768b 100644
+--- a/src/lib.rs
++++ b/src/lib.rs
+@@ -143,7 +143,7 @@ fn raise_unpackb_exception(msg: &str) -> *mut PyObject {
+ let err_msg =
+ PyUnicode_FromStringAndSize(msg.as_ptr() as *const c_char, msg.len() as isize);
+ let args = PyTuple_New(1);
+- PyTuple_SET_ITEM(args, 0, err_msg);
++ PyTuple_SetItem(args, 0, err_msg);
+ PyErr_SetObject(typeref::MsgpackDecodeError, args);
+ Py_DECREF(args);
+ };
+@@ -199,10 +199,10 @@ pub unsafe extern "C" fn unpackb(
+ if !kwnames.is_null() {
+ let tuple_size = PyTuple_GET_SIZE(kwnames);
+ for i in 0..tuple_size {
+- let arg = PyTuple_GET_ITEM(kwnames, i as Py_ssize_t);
+- if arg == typeref::EXT_HOOK {
++ let arg = PyTuple_GetItem(kwnames, i as Py_ssize_t);
++ if PyUnicode_Compare(arg, typeref::EXT_HOOK) == 0 {
+ ext_hook = Some(NonNull::new_unchecked(*args.offset(num_args + i)));
+- } else if arg == typeref::OPTION {
++ } else if PyUnicode_Compare(arg, typeref::OPTION) == 0 {
+ optsptr = Some(NonNull::new_unchecked(*args.offset(num_args + i)));
+ } else {
+ return raise_unpackb_exception("unpackb() got an unexpected keyword argument");
+@@ -247,15 +247,15 @@ pub unsafe extern "C" fn packb(
+ if !kwnames.is_null() {
+ let tuple_size = PyTuple_GET_SIZE(kwnames);
+ for i in 0..tuple_size {
+- let arg = PyTuple_GET_ITEM(kwnames, i as Py_ssize_t);
+- if arg == typeref::DEFAULT {
++ let arg = PyTuple_GetItem(kwnames, i as Py_ssize_t);
++ if PyUnicode_Compare(arg, typeref::DEFAULT) == 0 {
+ if unlikely!(default.is_some()) {
+ return raise_packb_exception(
+ "packb() got multiple values for argument: 'default'",
+ );
+ }
+ default = Some(NonNull::new_unchecked(*args.offset(num_args + i)));
+- } else if arg == typeref::OPTION {
++ } else if PyUnicode_Compare(arg, typeref::OPTION) == 0 {
+ if unlikely!(optsptr.is_some()) {
+ return raise_packb_exception(
+ "packb() got multiple values for argument: 'option'",
+diff --git a/src/serialize/datetime.rs b/src/serialize/datetime.rs
+index 63212d6..5ac2b2b 100644
+--- a/src/serialize/datetime.rs
++++ b/src/serialize/datetime.rs
+@@ -61,9 +61,14 @@ pub struct Time {
+
+ impl Time {
+ pub fn new(ptr: *mut pyo3::ffi::PyObject, opts: Opt) -> Result {
++ #[cfg(not(GraalPy))]
+ if unsafe { (*(ptr as *mut pyo3::ffi::PyDateTime_Time)).hastzinfo != 0 } {
+ return Err(TimeError::HasTimezone);
+ }
++ #[cfg(GraalPy)]
++ if unsafe { pyo3::ffi::PyDateTime_TIME_GET_TZINFO(ptr) != crate::typeref::NONE } {
++ return Err(TimeError::HasTimezone);
++ }
+ Ok(Time {
+ ptr: ptr,
+ opts: opts,
+@@ -114,23 +119,28 @@ impl std::fmt::Display for DateTimeError {
+ }
+
+ fn utcoffset(ptr: *mut pyo3::ffi::PyObject) -> Result {
++ #[cfg(not(GraalPy))]
+ if !unsafe { (*(ptr as *mut pyo3::ffi::PyDateTime_DateTime)).hastzinfo == 1 } {
+ return Ok(Offset::default());
+ }
+
+ let tzinfo = ffi!(PyDateTime_DATE_GET_TZINFO(ptr));
++ #[cfg(GraalPy)]
++ if unsafe { tzinfo == crate::typeref::NONE } {
++ return Ok(Offset::default());
++ }
+ let py_offset: *mut pyo3::ffi::PyObject;
+ if ffi!(PyObject_HasAttr(tzinfo, CONVERT_METHOD_STR)) == 1 {
+ // pendulum
+- py_offset = ffi!(PyObject_CallMethodNoArgs(ptr, UTCOFFSET_METHOD_STR));
++ py_offset = unsafe { pyo3::ffi::compat::PyObject_CallMethodNoArgs(ptr, UTCOFFSET_METHOD_STR) };
+ } else if ffi!(PyObject_HasAttr(tzinfo, NORMALIZE_METHOD_STR)) == 1 {
+ // pytz
+- let normalized = ffi!(PyObject_CallMethodOneArg(tzinfo, NORMALIZE_METHOD_STR, ptr));
+- py_offset = ffi!(PyObject_CallMethodNoArgs(normalized, UTCOFFSET_METHOD_STR));
++ let normalized = ffi!(PyObject_CallMethodObjArgs(tzinfo, NORMALIZE_METHOD_STR, ptr, std::ptr::null_mut::()));
++ py_offset = unsafe { pyo3::ffi::compat::PyObject_CallMethodNoArgs(normalized, UTCOFFSET_METHOD_STR) };
+ ffi!(Py_DECREF(normalized));
+ } else if ffi!(PyObject_HasAttr(tzinfo, DST_STR)) == 1 {
+ // dateutil/arrow, datetime.timezone.utc
+- py_offset = ffi!(PyObject_CallMethodOneArg(tzinfo, UTCOFFSET_METHOD_STR, ptr));
++ py_offset = ffi!(PyObject_CallMethodObjArgs(tzinfo, UTCOFFSET_METHOD_STR, ptr, std::ptr::null_mut::()));
+ } else {
+ return Err(DateTimeError::LibraryUnsupported);
+ }
+@@ -193,7 +203,10 @@ impl TimeLike for DateTime {
+
+ impl DateTimeLike for DateTime {
+ fn has_tz(&self) -> bool {
+- unsafe { (*(self.ptr as *mut pyo3::ffi::PyDateTime_DateTime)).hastzinfo == 1 }
++ #[cfg(not(GraalPy))]
++ return unsafe { (*(self.ptr as *mut pyo3::ffi::PyDateTime_DateTime)).hastzinfo == 1 };
++ #[cfg(GraalPy)]
++ return unsafe { pyo3::ffi::PyDateTime_TIME_GET_TZINFO(self.ptr) != crate::typeref::NONE };
+ }
+
+ fn offset(&self) -> Offset {
+diff --git a/src/serialize/numpy.rs b/src/serialize/numpy.rs
+index afc5cdf..4d007bd 100644
+--- a/src/serialize/numpy.rs
++++ b/src/serialize/numpy.rs
+@@ -392,8 +392,8 @@ impl NumpyDatetimeUnit {
+ fn from_pyobject(ptr: *mut PyObject) -> Self {
+ let dtype = ffi!(PyObject_GetAttr(ptr, DTYPE_STR));
+ let descr = ffi!(PyObject_GetAttr(dtype, DESCR_STR));
+- let el0 = ffi!(PyList_GET_ITEM(descr, 0));
+- let descr_str = ffi!(PyTuple_GET_ITEM(el0, 1));
++ let el0 = ffi!(PyList_GetItem(descr, 0));
++ let descr_str = ffi!(PyTuple_GetItem(el0, 1));
+ let uni = crate::unicode::unicode_to_str(descr_str).unwrap();
+ if uni.len() < 5 {
+ return Self::NaT;
+diff --git a/src/serialize/serializer.rs b/src/serialize/serializer.rs
+index 309e6e1..6f7dec7 100644
+--- a/src/serialize/serializer.rs
++++ b/src/serialize/serializer.rs
+@@ -864,7 +864,7 @@ impl Serialize for DictTupleKey {
+ let len = ffi!(PyTuple_GET_SIZE(self.ptr)) as usize;
+ let mut seq = serializer.serialize_seq(Some(len)).unwrap();
+ for i in 0..len {
+- let item = ffi!(PyTuple_GET_ITEM(self.ptr, i as isize));
++ let item = ffi!(PyTuple_GetItem(self.ptr, i as isize));
+ let value = DictKey::new(item, self.opts, self.recursion + 1);
+ seq.serialize_element(&value)?;
+ }
+diff --git a/src/serialize/tuple.rs b/src/serialize/tuple.rs
+index fa81cb6..9b66019 100644
+--- a/src/serialize/tuple.rs
++++ b/src/serialize/tuple.rs
+@@ -41,7 +41,7 @@ impl Serialize for Tuple {
+ let len = ffi!(PyTuple_GET_SIZE(self.ptr)) as usize;
+ let mut seq = serializer.serialize_seq(Some(len)).unwrap();
+ for i in 0..len {
+- let item = ffi!(PyTuple_GET_ITEM(self.ptr, i as isize));
++ let item = ffi!(PyTuple_GetItem(self.ptr, i as isize));
+ let value = PyObject::new(
+ item,
+ self.opts,
+diff --git a/src/serialize/writer.rs b/src/serialize/writer.rs
+index a790bdd..35346d9 100644
+--- a/src/serialize/writer.rs
++++ b/src/serialize/writer.rs
+@@ -27,7 +27,6 @@ impl BytesWriter {
+ pub fn finish(&mut self) -> NonNull {
+ unsafe {
+ std::ptr::write(self.buffer_ptr(), 0);
+- (*self.bytes.cast::()).ob_size = self.len as Py_ssize_t;
+ self.resize(self.len);
+ NonNull::new_unchecked(self.bytes as *mut PyObject)
+ }
+@@ -35,10 +34,14 @@ impl BytesWriter {
+
+ fn buffer_ptr(&self) -> *mut u8 {
+ unsafe {
+- std::mem::transmute::<*mut [c_char; 1], *mut u8>(std::ptr::addr_of_mut!(
++ #[cfg(not(GraalPy))]
++ return std::mem::transmute::<*mut [c_char; 1], *mut u8>(std::ptr::addr_of_mut!(
+ (*self.bytes).ob_sval
+ ))
+- .add(self.len)
++ .add(self.len);
++ #[cfg(GraalPy)]
++ return std::mem::transmute::<*mut i8, *mut u8>(PyBytes_AsString(self.bytes.cast::()))
++ .add(self.len);
+ }
+ }
+
+diff --git a/src/unicode.rs b/src/unicode.rs
+index 53aca09..552fa6c 100644
+--- a/src/unicode.rs
++++ b/src/unicode.rs
+@@ -6,6 +6,7 @@ use pyo3::ffi::*;
+
+ // see unicodeobject.h for documentation
+
++#[cfg(not(GraalPy))]
+ pub fn unicode_from_str(buf: &str) -> *mut PyObject {
+ if buf.is_empty() {
+ ffi!(Py_INCREF(EMPTY_UNICODE));
+@@ -27,6 +28,13 @@ pub fn unicode_from_str(buf: &str) -> *mut PyObject {
+ }
+ }
+
++#[cfg(GraalPy)]
++pub fn unicode_from_str(buf: &str) -> *mut PyObject {
++ unsafe {
++ PyUnicode_FromStringAndSize(buf.as_ptr() as *const i8, buf.len() as isize)
++ }
++}
++
+ fn pyunicode_ascii(buf: &str) -> *mut PyObject {
+ unsafe {
+ let ptr = ffi!(PyUnicode_New(buf.len() as isize, 127));
+@@ -80,6 +88,7 @@ fn pyunicode_fourbyte(buf: &str, num_chars: usize) -> *mut PyObject {
+
+ #[inline]
+ pub fn hash_str(op: *mut PyObject) -> Py_hash_t {
++ #[cfg(not(GraalPy))]
+ unsafe {
+ let data_ptr: *mut c_void = if (*op.cast::()).compact() == 1
+ && (*op.cast::()).ascii() == 1
+@@ -92,7 +101,11 @@ pub fn hash_str(op: *mut PyObject) -> Py_hash_t {
+ (*(op as *mut PyASCIIObject)).length * ((*(op as *mut PyASCIIObject)).kind()) as isize;
+ let hash = _Py_HashBytes(data_ptr, num_bytes);
+ (*op.cast::()).hash = hash;
+- hash
++ return hash;
++ }
++ #[cfg(GraalPy)]
++ unsafe {
++ return PyObject_Hash(op);
+ }
+ }
+
+@@ -109,19 +122,24 @@ pub fn unicode_to_str_via_ffi(op: *mut PyObject) -> Option<&'static str> {
+
+ #[inline]
+ pub fn unicode_to_str(op: *mut PyObject) -> Option<&'static str> {
++ #[cfg(not(GraalPy))]
+ unsafe {
+ if unlikely!((*op.cast::()).compact() == 0) {
+- unicode_to_str_via_ffi(op)
++ return unicode_to_str_via_ffi(op);
+ } else if (*op.cast::()).ascii() == 1 {
+ let ptr = op.cast::().offset(1) as *const u8;
+ let len = (*op.cast::()).length as usize;
+- Some(str_from_slice!(ptr, len))
++ return Some(str_from_slice!(ptr, len));
+ } else if (*op.cast::()).utf8_length != 0 {
+ let ptr = (*op.cast::()).utf8 as *const u8;
+ let len = (*op.cast::()).utf8_length as usize;
+- Some(str_from_slice!(ptr, len))
++ return Some(str_from_slice!(ptr, len));
+ } else {
+- unicode_to_str_via_ffi(op)
++ return unicode_to_str_via_ffi(op);
+ }
+ }
++ #[cfg(GraalPy)]
++ unsafe {
++ return unicode_to_str_via_ffi(op);
++ }
+ }
+diff --git a/src/util.rs b/src/util.rs
+index 2bcc32d..89faf1a 100644
+--- a/src/util.rs
++++ b/src/util.rs
+@@ -8,7 +8,7 @@ macro_rules! py_is {
+
+ macro_rules! ob_type {
+ ($obj:expr) => {
+- unsafe { (*($obj as *mut pyo3::ffi::PyObject)).ob_type }
++ unsafe { pyo3::ffi::Py_TYPE($obj as *mut pyo3::ffi::PyObject) }
+ };
+ }
+
+--
+2.43.0
+
diff --git a/graalpython/lib-graalpython/patches/pymupdf.patch b/graalpython/lib-graalpython/patches/pymupdf.patch
new file mode 100644
index 0000000000..27869eccd6
--- /dev/null
+++ b/graalpython/lib-graalpython/patches/pymupdf.patch
@@ -0,0 +1,124 @@
+diff --git a/graalpy-config b/graalpy-config
+new file mode 100755
+index 00000000..1f69f726
+--- /dev/null
++++ b/graalpy-config
+@@ -0,0 +1,78 @@
++#!/bin/sh
++
++# Adapted from CPython but deferring to GraalPy
++
++exit_with_usage ()
++{
++ echo "Usage: $0 --prefix|--exec-prefix|--includes|--libs|--cflags|--ldflags|--extension-suffix|--help|--abiflags|--configdir|--embed"
++ exit $1
++}
++
++if [ "$1" = "" ] ; then
++ exit_with_usage 1
++fi
++
++# Returns the actual prefix where this script was installed to.
++EXE=$(cd $(dirname "$0") && pwd -P)
++if which readlink >/dev/null 2>&1 ; then
++ if readlink -f "$RESULT" >/dev/null 2>&1; then
++ EXE=$(readlink -f "$RESULT")
++ fi
++fi
++EXE=$EXE/graalpy
++
++if ! test -x "$EXE" ; then
++ EXE=graalpy
++fi
++
++# Scan for --help or unknown argument.
++for ARG in $*
++do
++ case $ARG in
++ --help)
++ exit_with_usage 0
++ ;;
++ --embed)
++ echo "graalpy-config does not print embedding flags"
++ exit 1
++ ;;
++ --prefix|--exec-prefix|--includes|--libs|--cflags|--ldflags|--extension-suffix|--abiflags|--configdir)
++ ;;
++ *)
++ exit_with_usage 1
++ ;;
++ esac
++done
++
++for ARG in "$@"
++do
++ case "$ARG" in
++ --prefix)
++ $EXE -c "print(__import__('sysconfig').get_config_var('prefix'))"
++ ;;
++ --exec-prefix)
++ $EXE -c "print(__import__('sysconfig').get_config_var('exec_prefix'))"
++ ;;
++ --includes)
++ $EXE -c "from sysconfig import get_path; print('-I'+get_path('include'), '-I'+get_path('platinclude'))"
++ ;;
++ --cflags)
++ $EXE -c "import sysconfig as s; print('-I' + s.get_path('include'), '-I' + s.get_path('platinclude'), s.get_config_var('CFLAGS').replace('NDEBUG', 'DEBUG'), s.get_config_var('OPT').replace('NDEBUG', 'DEBUG'))"
++ ;;
++ --libs)
++ $EXE -c "import sysconfig as s; print('-L' + s.get_config_var('LIBDIR'))"
++ ;;
++ --ldflags)
++ $EXE -c "import sysconfig as s; print('-L' + s.get_config_var('LIBDIR'))"
++ ;;
++ --extension-suffix)
++ $EXE -c "import sysconfig as s; print(s.get_config_var('EXT_SUFFIX'))"
++ ;;
++ --abiflags)
++ $EXE -c "import sysconfig as s; print(s.get_config_var('ABIFLAGS'))"
++ ;;
++ --configdir)
++ echo ""
++ ;;
++esac
++done
+diff --git a/setup.py b/setup.py
+index 5fba2c97..3fe63b07 100755
+--- a/setup.py
++++ b/setup.py
+@@ -1452,0 +1452,35 @@
++if sys.implementation.name == "graalpy":
++ import os
++ import re
++ import subprocess
++ import shutil
++ import sysconfig
++ from pathlib import Path
++
++ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
++ wheel_directory = Path(wheel_directory).absolute()
++ sdir = Path(__file__).absolute().parent
++ python311 = shutil.which("python3.11")
++ if not python311:
++ raise RuntimeError("python3.11 must be available on the PATH for cross-compilation")
++ env = os.environ.copy()
++ env["PIPCL_PYTHON_CONFIG"] = str(sdir / "graalpy-config")
++ env["PYMUPDF_SETUP_PY_LIMITED_API"] = "1"
++ subprocess.run(
++ [python311, "setup.py", "bdist_wheel"],
++ env=env,
++ cwd=sdir,
++ check=True,
++ )
++ wheels = list((sdir / 'dist').glob('*.whl'))
++ assert len(wheels) == 1, f"Expected 1 wheel, found {len(wheels)}"
++ wheel = wheels[0]
++ assert "-cp311-abi3" in wheel.name, f"Expected wheel to be for CPython 3.11 ABI 3, got {wheel.name}"
++ graalpy_ext_suffix = sysconfig.get_config_var("EXT_SUFFIX")
++ m = re.match(r"\.graalpy(\d+[^\-]*)-(\d+)", sysconfig.get_config_var("EXT_SUFFIX"))
++ gpver = m[1]
++ cpver = m[2]
++ graalpy_wheel_tag = f"graalpy{cpver}-graalpy{gpver}_{cpver}_native"
++ name = wheel.name.replace("cp311-abi3", graalpy_wheel_tag)
++ shutil.copyfile(wheel, wheel_directory / name)
++ return str(name)
diff --git a/graalpython/lib-python/3/datetime.py b/graalpython/lib-python/3/datetime.py
index e284a733ef..c564969464 100644
--- a/graalpython/lib-python/3/datetime.py
+++ b/graalpython/lib-python/3/datetime.py
@@ -2642,3 +2642,5 @@ def _name_from_offset(delta):
# appropriate to maintain a single module level docstring and
# remove the following line.
from _datetime import __doc__
+
+import _polyglot_datetime # GraalPy change: register interop behavior on datetime as soon as datetime is defined
diff --git a/graalpython/org.graalvm.python.embedding.tools/src/org/graalvm/python/embedding/tools/vfs/VFSUtils.java b/graalpython/org.graalvm.python.embedding.tools/src/org/graalvm/python/embedding/tools/vfs/VFSUtils.java
index 46637889eb..0a99df77eb 100644
--- a/graalpython/org.graalvm.python.embedding.tools/src/org/graalvm/python/embedding/tools/vfs/VFSUtils.java
+++ b/graalpython/org.graalvm.python.embedding.tools/src/org/graalvm/python/embedding/tools/vfs/VFSUtils.java
@@ -44,6 +44,7 @@
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -64,6 +65,107 @@
public final class VFSUtils {
+ /**
+ * Patterns which should be excluded by default, like .gitignore or SCM files.
+ *
+ * - Misc: **/*~, **/#*#, **/.#*, **/%*%,
+ * **/._*
+ * - CVS: **/CVS, **/CVS/**, **/.cvsignore
+ * - RCS: **/RCS, **/RCS/**
+ * - SCCS: **/SCCS, **/SCCS/**
+ * - VSSercer: **/vssver.scc
+ * - MKS: **/project.pj
+ * - SVN: **/.svn, **/.svn/**
+ * - GNU: **/.arch-ids, **/.arch-ids/**
+ * - Bazaar: **/.bzr, **/.bzr/**
+ * - SurroundSCM: **/.MySCMServerInfo
+ * - Mac: **/.DS_Store
+ * - Serena Dimension: **/.metadata, **/.metadata/**
+ * - Mercurial: **/.hg, **/.hg/**
+ * - Git: **/.git, **/.git/**, **/.gitignore
+ * - Bitkeeper: **/BitKeeper, **/BitKeeper/**, **/ChangeSet,
+ * **/ChangeSet/**
+ * - Darcs: **/_darcs, **/_darcs/**, **/.darcsrepo,
+ * **/.darcsrepo/****/-darcs-backup*, **/.darcs-temp-mail
+ *
+ *
+ *
+ * The list is a copy of the one used in tools like the Maven JAR Plugin. @see DEFAULTEXCLUDES
+ */
+ private static final String[] DEFAULT_EXCLUDES = {
+ // Miscellaneous typical temporary files
+ "**/*~",
+ "**/#*#",
+ "**/.#*",
+ "**/%*%",
+ "**/._*",
+
+ // CVS
+ "**/CVS",
+ "**/CVS/**",
+ "**/.cvsignore",
+
+ // RCS
+ "**/RCS",
+ "**/RCS/**",
+
+ // SCCS
+ "**/SCCS",
+ "**/SCCS/**",
+
+ // Visual SourceSafe
+ "**/vssver.scc",
+
+ // MKS
+ "**/project.pj",
+
+ // Subversion
+ "**/.svn",
+ "**/.svn/**",
+
+ // Arch
+ "**/.arch-ids",
+ "**/.arch-ids/**",
+
+ // Bazaar
+ "**/.bzr",
+ "**/.bzr/**",
+
+ // SurroundSCM
+ "**/.MySCMServerInfo",
+
+ // Mac
+ "**/.DS_Store",
+
+ // Serena Dimensions Version 10
+ "**/.metadata",
+ "**/.metadata/**",
+
+ // Mercurial
+ "**/.hg",
+ "**/.hg/**",
+
+ // git
+ "**/.git",
+ "**/.git/**",
+ "**/.gitignore",
+
+ // BitKeeper
+ "**/BitKeeper",
+ "**/BitKeeper/**",
+ "**/ChangeSet",
+ "**/ChangeSet/**",
+
+ // darcs
+ "**/_darcs",
+ "**/_darcs/**",
+ "**/.darcsrepo",
+ "**/.darcsrepo/**",
+ "**/-darcs-backup*",
+ "**/.darcs-temp-mail"
+ };
+
public static final String VFS_ROOT = "org.graalvm.python.vfs";
public static final String VFS_VENV = "venv";
public static final String VFS_FILESLIST = "fileslist.txt";
@@ -152,23 +254,35 @@ public static void generateVFSFilesList(Path resourcesRoot, Path vfs, Set {
- String entry = null;
- if (Files.isDirectory(p)) {
- String dirPath = makeDirPath(p.toAbsolutePath());
- entry = dirPath.substring(rootEndIdx);
- } else if (Files.isRegularFile(p)) {
- entry = p.toAbsolutePath().toString().substring(rootEndIdx);
- }
- if (entry != null) {
- entry = normalizeResourcePath(entry);
- if (!ret.add(entry) && duplicateHandler != null) {
- duplicateHandler.accept(entry);
+ if (!shouldPathBeExcluded(p)) {
+ String entry = null;
+ if (Files.isDirectory(p)) {
+ String dirPath = makeDirPath(p.toAbsolutePath());
+ entry = dirPath.substring(rootEndIdx);
+ } else if (Files.isRegularFile(p)) {
+ entry = p.toAbsolutePath().toString().substring(rootEndIdx);
+ }
+ if (entry != null) {
+ entry = normalizeResourcePath(entry);
+ if (!ret.add(entry) && duplicateHandler != null) {
+ duplicateHandler.accept(entry);
+ }
}
}
});
}
}
+ private static boolean shouldPathBeExcluded(Path path) {
+ for (String glob : DEFAULT_EXCLUDES) {
+ var matcher = FileSystems.getDefault().getPathMatcher("glob:" + glob);
+ if (matcher.matches(path)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private static String makeDirPath(Path p) {
String ret = p.toString();
if (!ret.endsWith(File.separator)) {
diff --git a/graalpython/org.graalvm.python.embedding/src/org/graalvm/python/embedding/GraalPyResources.java b/graalpython/org.graalvm.python.embedding/src/org/graalvm/python/embedding/GraalPyResources.java
index 7efc01cd3a..fa26d06389 100644
--- a/graalpython/org.graalvm.python.embedding/src/org/graalvm/python/embedding/GraalPyResources.java
+++ b/graalpython/org.graalvm.python.embedding/src/org/graalvm/python/embedding/GraalPyResources.java
@@ -198,7 +198,12 @@ private GraalPyResources() {
* location
* /org.graalvm.python.vfs/src
- is set as the python sources location
*
- *
+ *
+ * When the virtual filesystem is located in other than the default resource directory,
+ * {@code org.graalvm.python.vfs}, i.e., using Maven or Gradle option {@code resourceDirectory},
+ * use {@link #contextBuilder(VirtualFileSystem)} and
+ * {@link VirtualFileSystem.Builder#resourceDirectory(String)} when building the
+ * {@link VirtualFileSystem}.
*
* @return a new {@link Context} instance
* @since 24.2.0
@@ -233,6 +238,12 @@ public static Context createContext() {
* }
* }
*
+ *
+ * When the virtual filesystem is located in other than the default resource directory,
+ * {@code org.graalvm.python.vfs}, i.e., using Maven or Gradle option {@code resourceDirectory},
+ * use {@link #contextBuilder(VirtualFileSystem)} and
+ * {@link VirtualFileSystem.Builder#resourceDirectory(String)} when building the
+ * {@link VirtualFileSystem}.
*
* @see PythonOptions
@@ -308,13 +319,14 @@ public static Context.Builder contextBuilder(VirtualFileSystem vfs) {
/**
* Creates a GraalPy context preconfigured with GraalPy and polyglot Context configuration
- * options for use with resources located in a real filesystem.
+ * options for use with resources located in an external directory in real filesystem.
*
* Following resource paths are preconfigured:
*
- * ${resourcesDirectory}/venv
- is set as the python virtual environment
+ * ${externalResourcesDirectory}/venv
- is set as the python virtual
+ * environment location
+ * ${externalResourcesDirectory}/src
- is set as the python sources
* location
- * ${resourcesDirectory}/src
- is set as the python sources location
*
*
*
@@ -343,19 +355,20 @@ public static Context.Builder contextBuilder(VirtualFileSystem vfs) {
*
*
*
- * @param resourcesDirectory the root directory with GraalPy specific embedding resources
+ * @param externalResourcesDirectory the root directory with GraalPy specific embedding
+ * resources
* @return a new {@link org.graalvm.polyglot.Context.Builder} instance
* @since 24.2.0
*/
- public static Context.Builder contextBuilder(Path resourcesDirectory) {
+ public static Context.Builder contextBuilder(Path externalResourcesDirectory) {
String execPath;
if (VirtualFileSystemImpl.isWindows()) {
- execPath = resourcesDirectory.resolve(VirtualFileSystemImpl.VFS_VENV).resolve("Scripts").resolve("python.exe").toAbsolutePath().toString();
+ execPath = externalResourcesDirectory.resolve(VirtualFileSystemImpl.VFS_VENV).resolve("Scripts").resolve("python.exe").toAbsolutePath().toString();
} else {
- execPath = resourcesDirectory.resolve(VirtualFileSystemImpl.VFS_VENV).resolve("bin").resolve("python").toAbsolutePath().toString();
+ execPath = externalResourcesDirectory.resolve(VirtualFileSystemImpl.VFS_VENV).resolve("bin").resolve("python").toAbsolutePath().toString();
}
- String srcPath = resourcesDirectory.resolve(VirtualFileSystemImpl.VFS_SRC).toAbsolutePath().toString();
+ String srcPath = externalResourcesDirectory.resolve(VirtualFileSystemImpl.VFS_SRC).toAbsolutePath().toString();
return createContextBuilder().
// allow all IO access
allowIO(IOAccess.ALL).
@@ -437,8 +450,9 @@ public static Path getNativeExecutablePath() {
* The structure of the created resource directory will stay the same like the embedded Python
* resources structure:
*
- * ${resourcesDirectory}/venv
- the python virtual environment location
- * ${resourcesDirectory}/src
- the python sources location
+ * ${externalResourcesDirectory}/venv
- the python virtual environment
+ * location
+ * ${externalResourcesDirectory}/src
- the python sources location
*
*
*
@@ -456,17 +470,17 @@ public static Path getNativeExecutablePath() {
*
*
* @param vfs the {@link VirtualFileSystem} from which resources are to be extracted
- * @param resourcesDirectory the target directory to extract the resources to
+ * @param externalResourcesDirectory the target directory to extract the resources to
* @throws IOException if resources isn't a directory
* @see #contextBuilder(Path)
* @see VirtualFileSystem.Builder#resourceLoadingClass(Class)
*
* @since 24.2.0
*/
- public static void extractVirtualFileSystemResources(VirtualFileSystem vfs, Path resourcesDirectory) throws IOException {
- if (Files.exists(resourcesDirectory) && !Files.isDirectory(resourcesDirectory)) {
- throw new IOException(String.format("%s has to be a directory", resourcesDirectory.toString()));
+ public static void extractVirtualFileSystemResources(VirtualFileSystem vfs, Path externalResourcesDirectory) throws IOException {
+ if (Files.exists(externalResourcesDirectory) && !Files.isDirectory(externalResourcesDirectory)) {
+ throw new IOException(String.format("%s has to be a directory", externalResourcesDirectory.toString()));
}
- vfs.impl.extractResources(resourcesDirectory);
+ vfs.impl.extractResources(externalResourcesDirectory);
}
}
diff --git a/graalpython/org.graalvm.python.gradle.plugin/src/main/java/org/graalvm/python/GraalPyGradlePlugin.java b/graalpython/org.graalvm.python.gradle.plugin/src/main/java/org/graalvm/python/GraalPyGradlePlugin.java
index f0ff56f0e1..0b964a9720 100644
--- a/graalpython/org.graalvm.python.gradle.plugin/src/main/java/org/graalvm/python/GraalPyGradlePlugin.java
+++ b/graalpython/org.graalvm.python.gradle.plugin/src/main/java/org/graalvm/python/GraalPyGradlePlugin.java
@@ -117,11 +117,11 @@ public void apply(Project project) {
if (extension.getPythonResourcesDirectory().isPresent() && extension.getExternalDirectory().isPresent()) {
throw new GradleException(
- "Cannot set both 'externalDirectory' and 'resourcesDirectory' at the same time. " +
+ "Cannot set both 'externalDirectory' and 'resourceDirectory' at the same time. " +
"New property 'externalDirectory' is a replacement for deprecated 'pythonResourcesDirectory'. " +
"If you want to deploy the virtual environment into physical filesystem, use 'externalDirectory'. " +
"The deployment of the external directory alongside the application is not handled by the GraalPy Maven plugin in such case." +
- "If you wish to bundle the virtual filesystem in Java resources, use 'resourcesDirectory'. " +
+ "If you wish to bundle the virtual filesystem in Java resources, use 'resourceDirectory'. " +
"For more details, please refer to https://www.graalvm.org/latest/reference-manual/python/Embedding-Build-Tools. ");
}
@@ -132,9 +132,9 @@ public void apply(Project project) {
// Run the vfsFilesListTask conditionally only if 'externalDirectory' is not set
if (!extension.getPythonResourcesDirectory().isPresent() && !extension.getExternalDirectory().isPresent()) {
if (!extension.getResourceDirectory().isPresent()) {
- proj.getLogger().info(String.format("Virtual filesystem is deployed to default resources directory '%s'. " +
+ proj.getLogger().warn(String.format("Virtual filesystem is deployed to default resources directory '%s'. " +
"This can cause conflicts if used with other Java libraries that also deploy GraalPy virtual filesystem. " +
- "Consider adding `resourcesDirectory = \"GRAALPY-VFS/${groupId}/${artifactId}\"` to your build.gradle script " +
+ "Consider adding `resourceDirectory = \"GRAALPY-VFS/${groupId}/${artifactId}\"` to your build.gradle script " +
"(replace the placeholders with values specific to your project), " +
"moving any existing sources from '%s' to '%s', and using VirtualFileSystem$Builder#resourceDirectory." +
"For more details, please refer to https://www.graalvm.org/latest/reference-manual/python/Embedding-Build-Tools. ",
@@ -192,7 +192,7 @@ private TaskProvider registerResourcesTask(Project project, Confi
t.getLogger().warn("The GraalPy plugin pythonHome configuration setting was deprecated and has no effect anymore.\n" +
"For execution in jvm mode, the python language home is always available.\n" +
"When building a native executable using GraalVM Native Image, then the full python language home is by default embedded into the native executable.\n" +
- "For more details, please refer to the documentation of GraalVM Native Image options IncludeLanguageResources and CopyLanguageResources documentation.");
+ "For more details, please refer to the documentation of GraalVM Native Image options IncludeLanguageResources and CopyLanguageResources.");
}
t.getPackages().set(extension.getPackages());
diff --git a/mx.graalpython/copyrights/overrides b/mx.graalpython/copyrights/overrides
index 9b09b5c78f..1a30d74d25 100644
--- a/mx.graalpython/copyrights/overrides
+++ b/mx.graalpython/copyrights/overrides
@@ -617,7 +617,11 @@ graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects
graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java,zippy.copyright
graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/PFloat.java,zippy.copyright
graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignObjectBuiltins.java,zippy.copyright
+graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignAbstractClassBuiltins.java,zippy.copyright
graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignBooleanBuiltins.java,zippy.copyright
+graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignExecutableBuiltins.java,zippy.copyright
+graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignInstantiableBuiltins.java,zippy.copyright
+graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignIterableBuiltins.java,zippy.copyright
graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignNumberBuiltins.java,zippy.copyright
graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/FrameBuiltins.java,zippy.copyright
graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/AbstractFunctionBuiltins.java,zippy.copyright
diff --git a/mx.graalpython/mx_graalpython.py b/mx.graalpython/mx_graalpython.py
index f86fdbf2a0..62542a46dd 100644
--- a/mx.graalpython/mx_graalpython.py
+++ b/mx.graalpython/mx_graalpython.py
@@ -170,6 +170,8 @@ def getArchivableResultsWithLib(self, *args, **kwargs):
if os.path.isfile(os.path.join(os.path.abspath(p), "cl.exe")):
mx.log("LIB and INCLUDE set, cl.exe on PATH, assuming this is a VS shell")
os.environ["DISTUTILS_USE_SDK"] = "1"
+ if not os.environ.get("MSSdk"):
+ os.environ["MSSdk"] = os.environ.get("WindowsSdkDir", "unset")
break
else:
mx.log("cl.exe not on PATH, not a VS shell")
diff --git a/mx.graalpython/suite.py b/mx.graalpython/suite.py
index 92ae8a88d7..d4269ffd74 100644
--- a/mx.graalpython/suite.py
+++ b/mx.graalpython/suite.py
@@ -9,7 +9,7 @@
"name": "graalpython",
"versionConflictResolution": "latest",
- "version": "25.0.0",
+ "version": "24.2.2",
"graalpython:pythonVersion": "3.11.7",
"release": False,
"groupId": "org.graalvm.python",
@@ -45,7 +45,7 @@
},
{
"name": "sdk",
- "version": "0f61c409358b6ecdb637d69410c8d3960b289e9c",
+ "version": "c9096be682f7aa67f5133fb098762e2152ff355f",
"subdir": True,
"urls": [
{"url": "https://github.com/oracle/graal", "kind": "git"},
@@ -53,7 +53,7 @@
},
{
"name": "tools",
- "version": "0f61c409358b6ecdb637d69410c8d3960b289e9c",
+ "version": "c9096be682f7aa67f5133fb098762e2152ff355f",
"subdir": True,
"urls": [
{"url": "https://github.com/oracle/graal", "kind": "git"},
@@ -61,7 +61,7 @@
},
{
"name": "sulong",
- "version": "0f61c409358b6ecdb637d69410c8d3960b289e9c",
+ "version": "c9096be682f7aa67f5133fb098762e2152ff355f",
"subdir": True,
"urls": [
{"url": "https://github.com/oracle/graal", "kind": "git"},
@@ -69,7 +69,7 @@
},
{
"name": "regex",
- "version": "0f61c409358b6ecdb637d69410c8d3960b289e9c",
+ "version": "c9096be682f7aa67f5133fb098762e2152ff355f",
"subdir": True,
"urls": [
{"url": "https://github.com/oracle/graal", "kind": "git"},
@@ -108,12 +108,12 @@
],
"digest": "sha512:16920fd41f398696c563417049472c0d81abb2d293ecb45bbbe97c12651669833e34eac238e2e4a6f8761ea58fb39806425d2741e88e8c3097fe2b5457ebf488",
},
- "XZ-5.2.6": {
+ "XZ-5.6.2": {
"urls": [
- "https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/xz-5.2.6.tar.gz",
+ "https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/xz-5.6.2.tar.gz",
],
"packedResource": True,
- "digest": "sha512:090958dd6c202c989746686094c86707ad4ae835026640080fc0a9d0fad699821b7d5cb3a67e6700661a0938818ba153662366f89ab8ec47e0bae4a3fe9b1961",
+ "digest": "sha512:c32c32c95e3541b906e0284e66a953ace677e0ce6af2084e7b122600047bf7542c1b0fabb5909b19ff79fba6def530be674df1c675b22a47a8d57f3f0b736a82",
},
"BOUNCYCASTLE-PROVIDER": {
"digest": "sha512:fb10c3c089921c8173ad285329f730e0e78de175d1b50b9bdd79c6a85a265af9b3331caa0c1ed57e5f47047319ce3b0f3bb5def0a3db9cccf2755cc95e145e52",
@@ -636,10 +636,10 @@
"bin/",
],
"cmakeConfig": {
- "XZ_SRC": "",
+ "XZ_SRC": "",
"XZ_VERSION_MAJOR": "5",
- "XZ_VERSION_MINOR": "2",
- "XZ_VERSION_PATCH": "6",
+ "XZ_VERSION_MINOR": "6",
+ "XZ_VERSION_PATCH": "2",
},
"os_arch": {
"windows": {
@@ -654,7 +654,7 @@
},
},
"buildDependencies": [
- "XZ-5.2.6",
+ "XZ-5.6.2",
],
},
diff --git a/mx.graalpython/verify_patches.py b/mx.graalpython/verify_patches.py
index 1ebc371694..8aa4e84c10 100644
--- a/mx.graalpython/verify_patches.py
+++ b/mx.graalpython/verify_patches.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2023, 2025, 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
@@ -45,6 +45,7 @@
# Approved license identifiers in SPDX "short identifier" format
ALLOWED_LICENSES = {
+ 'UPL', # https://spdx.org/licenses/UPL-1.0.html
'MIT', # https://spdx.org/licenses/MIT.html
'BSD-3-Clause', # https://spdx.org/licenses/BSD-3-Clause.html
'BSD-2-Clause', # https://spdx.org/licenses/BSD-2-Clause.html
diff --git a/scripts/wheelbuilder/build_wheels.py b/scripts/wheelbuilder/build_wheels.py
index 605032e50d..b6a00c7cce 100644
--- a/scripts/wheelbuilder/build_wheels.py
+++ b/scripts/wheelbuilder/build_wheels.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2023, 2025, 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
@@ -100,6 +100,10 @@ def create_venv():
subprocess.check_call([binary, "-m", "venv", "graalpy"])
print("Installing wheel with", pip, flush=True)
subprocess.check_call([pip, "install", "wheel"])
+ print("Installing paatch to provide patch.exe", flush=True)
+ p = subprocess.run([pip, "install", "paatch"])
+ if p.returncode != 0:
+ print("Installing paatch failed, assuming a GNU patch compatible binary is on PATH", flush=True)
return pip