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> 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> 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> 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> 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> 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> 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