diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_array.py b/graalpython/com.oracle.graal.python.test/src/tests/test_array.py index f0cb704814..a3b58d0107 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_array.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_array.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # The Universal Permissive License (UPL), Version 1.0 @@ -37,6 +37,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import copy +import types from array import array from tests.util import storage_to_native @@ -112,6 +114,52 @@ def test_array_native_storage(): a.insert(1, -1) assert a == array('l', [1, -1, 3]) + +def test_copy(): + a = array('l', [1, 2, 3]) + b = a.__copy__() + c = copy.copy(a) + assert type(b) is array + assert b == a + assert c == a + assert b is not a + assert c is not a + a[0] = 42 + assert b == array('l', [1, 2, 3]) + assert c == array('l', [1, 2, 3]) + + +def test_copy_native_storage(): + a = array('l', [1, 2, 3]) + storage_to_native(a) + b = a.__copy__() + assert b == a + a[1] = 42 + assert b == array('l', [1, 2, 3]) + + +def test_deepcopy(): + a = array('l', [1, 2, 3]) + b = a.__deepcopy__({}) + c = copy.deepcopy(a) + assert type(b) is array + assert b == a + assert c == a + assert b is not a + assert c is not a + a[0] = 42 + assert b == array('l', [1, 2, 3]) + assert c == array('l', [1, 2, 3]) + + +def test_class_getitem(): + alias = array[int] + assert isinstance(alias, types.GenericAlias) + assert alias.__origin__ is array + assert alias.__args__ == (int,) + assert array.__class_getitem__(str).__args__ == (str,) + + def test_mul(): a = array('l', [1, 2, 3]) assert len(a * 0) == 0 @@ -119,4 +167,4 @@ def test_mul(): b = a * 1 a[2] = 42 assert list(a) == [1, 2, 42] - assert list(b) == [1, 2, 3] \ No newline at end of file + assert list(b) == [1, 2, 3] diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_itertools.py b/graalpython/com.oracle.graal.python.test/src/tests/test_itertools.py index edda296886..e27de87f5b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_itertools.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_itertools.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2022, Oracle and/or its affiliates. +# Copyright (c) 2019, 2026, Oracle and/or its affiliates. # Copyright (C) 1996-2017 Python Software Foundation # # Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 @@ -104,7 +104,7 @@ def keyfunc(obj): def test_generators(self): # test that generators are accepted like input values - + def g(seqn): for i in seqn: yield i @@ -112,7 +112,7 @@ def g(seqn): def g2(seqn): for i in seqn: yield (i, i) - + self.assertEqual(list(accumulate(g([1,2]))), [1, 3]) self.assertEqual(list(chain(g([1,2]), g([3,4]))), [1, 2, 3, 4]) self.assertEqual(list(combinations(g([1,2]), 2)), [(1, 2)]) @@ -130,6 +130,10 @@ def g2(seqn): self.assertEqual(list(tee(g([1, 2]))[0]), [1, 2]) self.assertEqual(list(zip_longest(g2([2,3]))), [((2, 2),), ((3, 3),)]) + def test_islice_negative_stop(self): + self.assertRaises(ValueError, islice, count(), -1) + self.assertRaises(ValueError, islice, count(), 0, -1) + @unittest.skipIf(sys.implementation.name == 'cpython' and sys.version_info[0:2] < (3, 10), "skipping for cPython versions < 3.10") def test_pairwise_drained(self): p = pairwise("abcd") diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_traceback.py b/graalpython/com.oracle.graal.python.test/src/tests/test_traceback.py index 84616e58a9..e150f798ed 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_traceback.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_traceback.py @@ -37,6 +37,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import ast +import linecache import subprocess import sys @@ -104,6 +105,32 @@ def test(): ) +def test_traceback_from_ast_without_end_positions(): + import traceback + + filename = "" + source = "def f():\n value = None\n raise AssertionError\n" + linecache.cache[filename] = (len(source), None, source.splitlines(True), filename) + tree = ast.parse(source, filename) + for node in ast.walk(tree): + if hasattr(node, "end_lineno"): + node.end_lineno = None + node.end_col_offset = None + + namespace = {} + exec(compile(tree, filename, "exec"), namespace) + try: + namespace["f"]() + except AssertionError: + stack = traceback.TracebackException(*sys.exc_info()).stack + else: + assert False, "generated function did not raise" + + assert stack[-1].name == "f" + assert stack[-1].lineno == 3 + assert stack[-1].line == "raise AssertionError" + + def test_basic_traceback_generator(): def foo(): yield 1 diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayBuiltins.java index f433eecc0e..8ffe4b2714 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayBuiltins.java @@ -43,6 +43,9 @@ import static com.oracle.graal.python.nodes.ErrorMessages.S_TAKES_AT_MOST_D_ARGUMENTS_D_GIVEN; import static com.oracle.graal.python.nodes.ErrorMessages.S_TAKES_NO_KEYWORD_ARGS; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DICT__; +import static com.oracle.graal.python.nodes.SpecialMethodNames.J___CLASS_GETITEM__; +import static com.oracle.graal.python.nodes.SpecialMethodNames.J___COPY__; +import static com.oracle.graal.python.nodes.SpecialMethodNames.J___DEEPCOPY__; import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REDUCE_EX__; import static com.oracle.graal.python.nodes.StringLiterals.T_COMMA_SPACE; import static com.oracle.graal.python.nodes.StringLiterals.T_LBRACKET; @@ -984,6 +987,51 @@ static Object bufferinfo(PArray self, } } + @Builtin(name = J___COPY__, minNumOfPositionalArgs = 1) + @GenerateNodeFactory + abstract static class CopyNode extends PythonUnaryBuiltinNode { + @Specialization + static PArray copy(PArray self, + @CachedLibrary(limit = "2") PythonBufferAccessLibrary bufferLib, + @Bind PythonLanguage language) { + return copyArray(self, bufferLib, language); + } + } + + @Builtin(name = J___DEEPCOPY__, minNumOfPositionalArgs = 2, parameterNames = {"$self", "memo"}) + @GenerateNodeFactory + abstract static class DeepCopyNode extends PythonBinaryBuiltinNode { + @Specialization + static PArray copy(PArray self, @SuppressWarnings("unused") Object memo, + @CachedLibrary(limit = "2") PythonBufferAccessLibrary bufferLib, + @Bind PythonLanguage language) { + return copyArray(self, bufferLib, language); + } + } + + private static PArray copyArray(PArray self, PythonBufferAccessLibrary bufferLib, PythonLanguage language) { + int length = self.getLength(); + PArray newArray; + try { + newArray = PFactory.createArray(language, self.getFormatString(), self.getFormat(), length); + } catch (OverflowException e) { + // It is a copy of an existing array, the length cannot overflow. + throw CompilerDirectives.shouldNotReachHere(); + } + bufferLib.readIntoBuffer(self.getBuffer(), 0, newArray.getBuffer(), 0, self.getBytesLength(), bufferLib); + return newArray; + } + + @Builtin(name = J___CLASS_GETITEM__, minNumOfPositionalArgs = 2, isClassmethod = true) + @GenerateNodeFactory + abstract static class ClassGetItemNode extends PythonBinaryBuiltinNode { + @Specialization + static Object classGetItem(Object cls, Object key, + @Bind PythonLanguage language) { + return PFactory.createGenericAlias(language, cls, key); + } + } + @Builtin(name = J_APPEND, minNumOfPositionalArgs = 2) @GenerateNodeFactory abstract static class AppendNode extends PythonBinaryBuiltinNode { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/IsliceBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/IsliceBuiltins.java index 10f0c7ce3f..27f434159b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/IsliceBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/IsliceBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -157,10 +157,10 @@ static Object constructOne(VirtualFrame frame, Object cls, Object[] args, PKeywo stopNotInt.enter(inliningTarget); throw raiseNode.raise(inliningTarget, ValueError, S_FOR_ISLICE_MUST_BE, "Indices"); } - } - if (stop < -1 || stop > SysModuleBuiltins.MAXSIZE) { - stopWrongValue.enter(inliningTarget); - throw raiseNode.raise(inliningTarget, ValueError, S_FOR_ISLICE_MUST_BE, "Indices"); + if (stop < 0 || stop > SysModuleBuiltins.MAXSIZE) { + stopWrongValue.enter(inliningTarget); + throw raiseNode.raise(inliningTarget, ValueError, S_FOR_ISLICE_MUST_BE, "Indices"); + } } } else if (argsLen2.profile(inliningTarget, args.length == 3) || argsLen3.profile(inliningTarget, args.length == 4)) { if (args[1] != PNone.NONE) { @@ -180,6 +180,10 @@ static Object constructOne(VirtualFrame frame, Object cls, Object[] args, PKeywo stopNotInt.enter(inliningTarget); throw raiseNode.raise(inliningTarget, ValueError, S_FOR_ISLICE_MUST_BE, "Stop argument"); } + if (stop < 0 || stop > SysModuleBuiltins.MAXSIZE) { + wrongValue.enter(inliningTarget); + throw raiseNode.raise(inliningTarget, ValueError, S_FOR_ISLICE_MUST_BE, "Indices"); + } } if (start < 0 || stop < -1 || start > SysModuleBuiltins.MAXSIZE || stop > SysModuleBuiltins.MAXSIZE) { wrongValue.enter(inliningTarget); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/RootNodeCompiler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/RootNodeCompiler.java index 1b45f71242..7cf9b6b4d1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/RootNodeCompiler.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/RootNodeCompiler.java @@ -828,17 +828,18 @@ void beginRootSourceSection(SSTNode node, Builder b) { private static void beginSourceSectionInner(Builder b, SourceRange sourceRange) { if (sourceRange.startLine >= 1 && sourceRange != SourceRange.ARTIFICIAL_RANGE) { if (sourceRange.startColumn >= 0 && sourceRange.endLine >= sourceRange.startLine && sourceRange.endColumn >= 0) { - if (sourceRange.endColumn > 0) { - b.beginSourceSection(sourceRange.startLine, sourceRange.startColumn + 1, sourceRange.endLine, sourceRange.endColumn); - } else { + int startColumn = sourceRange.startColumn + 1; + int endColumn = sourceRange.endColumn > 0 ? sourceRange.endColumn : 1; + if (sourceRange.endLine == sourceRange.startLine && endColumn < startColumn) { /* - * Truffle doesn't allow including an empty line with no characters, so we just - * include the first character to have at least something. It's not correct, but - * these cases are very rare, it occurs primarily in string consituents of - * top-level multiline format strings. + * Truffle doesn't allow source sections with empty or inverted ranges. These are + * rare, but can occur for string constituents of top-level multiline format + * strings and for AST-created code without end-position metadata. */ - b.beginSourceSection(sourceRange.startLine, sourceRange.startColumn + 1, sourceRange.endLine, 1); + b.beginSourceSection(sourceRange.startLine); + return; } + b.beginSourceSection(sourceRange.startLine, startColumn, sourceRange.endLine, endColumn); } else { b.beginSourceSection(sourceRange.startLine); } diff --git a/graalpython/lib-graalpython/patches/librt-0.9.0.patch b/graalpython/lib-graalpython/patches/librt-0.9.0.patch index 53c99e6bc3..0a500869dc 100644 --- a/graalpython/lib-graalpython/patches/librt-0.9.0.patch +++ b/graalpython/lib-graalpython/patches/librt-0.9.0.patch @@ -1,8 +1,70 @@ +diff --git a/CPy.h b/CPy.h +index 3a7a08a..1895da3 100644 +--- a/CPy.h ++++ b/CPy.h +@@ -333,6 +333,7 @@ static inline bool CPyTagged_IsLe(CPyTagged left, CPyTagged right) { + } + + static inline int64_t CPyLong_AsInt64(PyObject *o) { ++#ifndef GRAALPY_VERSION_NUM + if (likely(PyLong_Check(o))) { + PyLongObject *lobj = (PyLongObject *)o; + #if CPY_3_12_FEATURES +@@ -349,11 +350,13 @@ static inline int64_t CPyLong_AsInt64(PyObject *o) { + } + #endif + } ++#endif + // Slow path + return CPyLong_AsInt64_(o); + } + + static inline int32_t CPyLong_AsInt32(PyObject *o) { ++#ifndef GRAALPY_VERSION_NUM + if (likely(PyLong_Check(o))) { + #if CPY_3_12_FEATURES + PyLongObject *lobj = (PyLongObject *)o; +@@ -375,11 +378,13 @@ static inline int32_t CPyLong_AsInt32(PyObject *o) { + } + #endif + } ++#endif + // Slow path + return CPyLong_AsInt32_(o); + } + + static inline int16_t CPyLong_AsInt16(PyObject *o) { ++#ifndef GRAALPY_VERSION_NUM + if (likely(PyLong_Check(o))) { + #if CPY_3_12_FEATURES + PyLongObject *lobj = (PyLongObject *)o; +@@ -405,11 +410,13 @@ static inline int16_t CPyLong_AsInt16(PyObject *o) { + } + #endif + } ++#endif + // Slow path + return CPyLong_AsInt16_(o); + } + + static inline uint8_t CPyLong_AsUInt8(PyObject *o) { ++#ifndef GRAALPY_VERSION_NUM + if (likely(PyLong_Check(o))) { + #if CPY_3_12_FEATURES + PyLongObject *lobj = (PyLongObject *)o; +@@ -435,6 +442,7 @@ static inline uint8_t CPyLong_AsUInt8(PyObject *o) { + } + #endif + } ++#endif + // Slow path + return CPyLong_AsUInt8_(o); + } diff --git a/mypyc_util.h b/mypyc_util.h -index 115c309..d0c825f 100644 +index 115c309..5b2315d 100644 --- a/mypyc_util.h +++ b/mypyc_util.h -@@ -82,7 +82,7 @@ +@@ -82,21 +82,17 @@ static inline void CPy_INCREF_NO_IMM(PyObject *op) { @@ -11,11 +73,96 @@ index 115c309..d0c825f 100644 } static inline void CPy_DECREF_NO_IMM(PyObject *op) + { +- if ((Py_SET_REFCNT(op, Py_REFCNT(op) - 1), Py_REFCNT(op)) == 0) { +- _Py_Dealloc(op); +- } ++ Py_DECREF(op); + } + + static inline void CPy_XDECREF_NO_IMM(PyObject *op) + { +- if (op != NULL && (Py_SET_REFCNT(op, Py_REFCNT(op) - 1), Py_REFCNT(op)) == 0) { +- _Py_Dealloc(op); +- } ++ Py_XDECREF(op); + } + + #define CPy_INCREF_NO_IMM(op) CPy_INCREF_NO_IMM((PyObject *)(op)) +diff --git a/pythonsupport.c b/pythonsupport.c +index 0a99f0a..1a442bb 100644 +--- a/pythonsupport.c ++++ b/pythonsupport.c +@@ -108,7 +108,46 @@ init_subclass(PyTypeObject *type, PyObject *kwds) + return 0; + } + +-#if CPY_3_12_FEATURES ++#ifdef GRAALPY_VERSION_NUM ++ ++Py_ssize_t ++CPyLong_AsSsize_tAndOverflow_(PyObject *vv, int *overflow) ++{ ++ Py_ssize_t res; ++ ++ *overflow = 0; ++ ++ res = PyLong_AsSsize_t(vv); ++ if (res == -1 && PyErr_Occurred()) { ++ PyObject *zero; ++ int is_negative; ++ ++ if (!PyErr_ExceptionMatches(PyExc_OverflowError)) { ++ return -1; ++ } ++ PyErr_Clear(); ++ ++ zero = PyLong_FromLong(0); ++ if (zero == NULL) { ++ return -1; ++ } ++ is_negative = PyObject_RichCompareBool(vv, zero, Py_LT); ++ Py_DECREF(zero); ++ if (is_negative < 0) { ++ return -1; ++ } ++ *overflow = is_negative ? -1 : 1; ++ return -1; ++ } ++ ++ if ((size_t)res > CPY_TAGGED_MAX && (res >= 0 || res < CPY_TAGGED_MIN)) { ++ *overflow = res < 0 ? -1 : 1; ++ return -1; ++ } ++ return res; ++} ++ ++#elif CPY_3_12_FEATURES + + // Slow path of CPyLong_AsSsize_tAndOverflow (non-inlined) + Py_ssize_t diff --git a/pythonsupport.h b/pythonsupport.h -index 6f38a9b..59d5e67 100644 +index 6f38a9b..0e89511 100644 --- a/pythonsupport.h +++ b/pythonsupport.h -@@ -117,6 +117,7 @@ CPyLong_AsSsize_tAndOverflow(PyObject *vv, int *overflow) +@@ -40,7 +40,15 @@ int init_subclass(PyTypeObject *type, PyObject *kwds); + Py_ssize_t + CPyLong_AsSsize_tAndOverflow_(PyObject *vv, int *overflow); + +-#if CPY_3_12_FEATURES ++#ifdef GRAALPY_VERSION_NUM ++ ++static inline Py_ssize_t ++CPyLong_AsSsize_tAndOverflow(PyObject *vv, int *overflow) ++{ ++ return CPyLong_AsSsize_tAndOverflow_(vv, overflow); ++} ++ ++#elif CPY_3_12_FEATURES + + static inline Py_ssize_t + CPyLong_AsSsize_tAndOverflow(PyObject *vv, int *overflow) +@@ -117,6 +125,7 @@ CPyLong_AsSsize_tAndOverflow(PyObject *vv, int *overflow) #endif // Adapted from listobject.c in Python 3.7.0 @@ -23,7 +170,7 @@ index 6f38a9b..59d5e67 100644 static int list_resize(PyListObject *self, Py_ssize_t newsize) { -@@ -162,6 +163,7 @@ list_resize(PyListObject *self, Py_ssize_t newsize) +@@ -162,6 +171,7 @@ list_resize(PyListObject *self, Py_ssize_t newsize) self->allocated = new_allocated; return 0; } @@ -31,21 +178,19 @@ index 6f38a9b..59d5e67 100644 // Changed to use PyList_SetSlice instead of the internal list_ass_slice static PyObject * -@@ -182,6 +184,8 @@ list_pop_impl(PyListObject *self, Py_ssize_t index) +@@ -182,6 +192,7 @@ list_pop_impl(PyListObject *self, Py_ssize_t index) return NULL; } v = PySequence_Fast_ITEMS((PyObject*)self)[index]; -+ Py_INCREF(v); +#if 0 // GraalPy change if (index == Py_SIZE(self) - 1) { status = list_resize(self, Py_SIZE(self) - 1); if (status >= 0) -@@ -189,7 +193,7 @@ list_pop_impl(PyListObject *self, Py_ssize_t index) +@@ -189,6 +200,7 @@ list_pop_impl(PyListObject *self, Py_ssize_t index) else return NULL; } -- Py_INCREF(v); +#endif + Py_INCREF(v); status = PyList_SetSlice((PyObject *)self, index, index+1, (PyObject *)NULL); if (status < 0) { - Py_DECREF(v); diff --git a/graalpython/lib-graalpython/patches/metadata.toml b/graalpython/lib-graalpython/patches/metadata.toml index 7c8e581cc9..14d47f6d52 100644 --- a/graalpython/lib-graalpython/patches/metadata.toml +++ b/graalpython/lib-graalpython/patches/metadata.toml @@ -988,7 +988,6 @@ version = '>= 2, < 2.0.3' install-priority = 0 [[uvloop.rules]] -version = '<= 0.19.0' patch = 'uvloop.patch' license = 'MIT'