diff --git a/graalpython/lib-graalpython/patches/coverage-7.10.2.patch b/graalpython/lib-graalpython/patches/coverage-7.10.2.patch new file mode 100644 index 0000000000..60c6b033bb --- /dev/null +++ b/graalpython/lib-graalpython/patches/coverage-7.10.2.patch @@ -0,0 +1,92 @@ +diff --git a/coverage/bytecode.py b/coverage/bytecode.py +index a564a41..ad36067 100644 +--- a/coverage/bytecode.py ++++ b/coverage/bytecode.py +@@ -6,7 +6,6 @@ + from __future__ import annotations + + import collections +-import dis + + from types import CodeType + from typing import Iterable, Optional +@@ -33,7 +32,7 @@ def op_set(*op_names: str) -> set[int]: + + The names might not exist in this version of Python, skip those if not. + """ +- return {op for name in op_names if (op := dis.opmap.get(name))} ++ return set() + + + # Opcodes that are unconditional jumps elsewhere. +@@ -120,6 +119,7 @@ def branch_trails(code: CodeType) -> TBranchTrails: + arc from the original instruction's line to the new source line. + + """ ++ return {} + the_trails: TBranchTrails = collections.defaultdict(lambda:collections.defaultdict(set)) + iwalker = InstructionWalker(code) + for inst in iwalker.walk(follow_jumps=False): +diff --git a/coverage/pytracer.py b/coverage/pytracer.py +index 31b4866..644cd4b 100644 +--- a/coverage/pytracer.py ++++ b/coverage/pytracer.py +@@ -6,7 +6,6 @@ + from __future__ import annotations + + import atexit +-import dis + import itertools + import sys + import threading +@@ -39,11 +38,11 @@ set_TArc = set[TArc] + + # We need the YIELD_VALUE opcode below, in a comparison-friendly form. + # PYVERSIONS: RESUME is new in Python3.11 +-RESUME = dis.opmap.get("RESUME") +-RETURN_VALUE = dis.opmap["RETURN_VALUE"] ++RESUME = None ++RETURN_VALUE = 1 + if RESUME is None: +- YIELD_VALUE = dis.opmap["YIELD_VALUE"] +- YIELD_FROM = dis.opmap["YIELD_FROM"] ++ YIELD_VALUE = 0 ++ YIELD_FROM = 0 + YIELD_FROM_OFFSET = 0 if env.PYPY else 2 + else: + YIELD_VALUE = YIELD_FROM = YIELD_FROM_OFFSET = -1 +@@ -107,6 +106,7 @@ class PyTracer(Tracer): + # [2] The last line number executed in this frame. + # [3] Boolean: did this frame start a new context? + self.data_stack: list[tuple[TTraceFileData | None, str | None, TLineNo, bool]] = [] ++ self.resumable_frames: set[int] = set() + self.thread: threading.Thread | None = None + self.stopped = False + self._activity = False +@@ -247,10 +247,12 @@ class PyTracer(Tracer): + if RESUME is not None: + # The current opcode is guaranteed to be RESUME. The argument + # determines what kind of resume it is. +- oparg = frame.f_code.co_code[frame.f_lasti + 1] ++ oparg = 0 + real_call = (oparg == 0) + else: +- real_call = (getattr(frame, "f_lasti", -1) < 0) ++ frame_id = id(frame) ++ real_call = frame_id not in self.resumable_frames ++ self.resumable_frames.add(frame_id) + if real_call: + self.last_line = -frame.f_code.co_firstlineno + else: +@@ -271,8 +273,9 @@ class PyTracer(Tracer): + if self.trace_arcs and self.cur_file_data: + # Record an arc leaving the function, but beware that a + # "return" event might just mean yielding from a generator. +- code = frame.f_code.co_code +- lasti = frame.f_lasti ++ resumable = frame.f_code.co_flags & (0x20 | 0x80 | 0x100 | 0x200) ++ code = (YIELD_VALUE,) if resumable else (RETURN_VALUE,) ++ lasti = 0 + if RESUME is not None: + if len(code) == lasti + 2: + # A return from the end of a code object is a real return. diff --git a/graalpython/lib-graalpython/patches/coverage-7.10.3.patch b/graalpython/lib-graalpython/patches/coverage-7.10.3.patch new file mode 100644 index 0000000000..cfc4760877 --- /dev/null +++ b/graalpython/lib-graalpython/patches/coverage-7.10.3.patch @@ -0,0 +1,92 @@ +diff --git a/coverage/bytecode.py b/coverage/bytecode.py +index a61f052..c5e9e5b 100644 +--- a/coverage/bytecode.py ++++ b/coverage/bytecode.py +@@ -6,7 +6,6 @@ + from __future__ import annotations + + import collections +-import dis + from collections.abc import Iterator + from types import CodeType + from typing import Iterable, Optional +@@ -32,7 +31,7 @@ def op_set(*op_names: str) -> set[int]: + + The names might not exist in this version of Python, skip those if not. + """ +- return {op for name in op_names if (op := dis.opmap.get(name))} ++ return set() + + + # Opcodes that are unconditional jumps elsewhere. +@@ -116,6 +115,7 @@ def branch_trails(code: CodeType) -> TBranchTrails: + arc from the original instruction's line to the new source line. + + """ ++ return {} + the_trails: TBranchTrails = collections.defaultdict( + lambda: collections.defaultdict(set) + ) +diff --git a/coverage/pytracer.py b/coverage/pytracer.py +index 9a24e4f..d9b13db 100644 +--- a/coverage/pytracer.py ++++ b/coverage/pytracer.py +@@ -6,7 +6,6 @@ + from __future__ import annotations + + import atexit +-import dis + import itertools + import sys + import threading +@@ -37,11 +36,11 @@ set_TArc = set[TArc] + + # We need the YIELD_VALUE opcode below, in a comparison-friendly form. + # PYVERSIONS: RESUME is new in Python3.11 +-RESUME = dis.opmap.get("RESUME") +-RETURN_VALUE = dis.opmap["RETURN_VALUE"] ++RESUME = None ++RETURN_VALUE = 1 + if RESUME is None: +- YIELD_VALUE = dis.opmap["YIELD_VALUE"] +- YIELD_FROM = dis.opmap["YIELD_FROM"] ++ YIELD_VALUE = 0 ++ YIELD_FROM = 0 + YIELD_FROM_OFFSET = 0 if env.PYPY else 2 + else: + YIELD_VALUE = YIELD_FROM = YIELD_FROM_OFFSET = -1 +@@ -105,6 +104,7 @@ class PyTracer(Tracer): + # [2] The last line number executed in this frame. + # [3] Boolean: did this frame start a new context? + self.data_stack: list[tuple[TTraceFileData | None, str | None, TLineNo, bool]] = [] ++ self.resumable_frames: set[int] = set() + self.thread: threading.Thread | None = None + self.stopped = False + self._activity = False +@@ -245,10 +245,12 @@ class PyTracer(Tracer): + if RESUME is not None: + # The current opcode is guaranteed to be RESUME. The argument + # determines what kind of resume it is. +- oparg = frame.f_code.co_code[frame.f_lasti + 1] ++ oparg = 0 + real_call = (oparg == 0) + else: +- real_call = (getattr(frame, "f_lasti", -1) < 0) ++ frame_id = id(frame) ++ real_call = frame_id not in self.resumable_frames ++ self.resumable_frames.add(frame_id) + if real_call: + self.last_line = -frame.f_code.co_firstlineno + else: +@@ -269,8 +271,9 @@ class PyTracer(Tracer): + if self.trace_arcs and self.cur_file_data: + # Record an arc leaving the function, but beware that a + # "return" event might just mean yielding from a generator. +- code = frame.f_code.co_code +- lasti = frame.f_lasti ++ resumable = frame.f_code.co_flags & (0x20 | 0x80 | 0x100 | 0x200) ++ code = (YIELD_VALUE,) if resumable else (RETURN_VALUE,) ++ lasti = 0 + if RESUME is not None: + if len(code) == lasti + 2: + # A return from the end of a code object is a real return. diff --git a/graalpython/lib-graalpython/patches/coverage-7.10.5.patch b/graalpython/lib-graalpython/patches/coverage-7.10.5.patch new file mode 100644 index 0000000000..dbcba79559 --- /dev/null +++ b/graalpython/lib-graalpython/patches/coverage-7.10.5.patch @@ -0,0 +1,92 @@ +diff --git a/coverage/bytecode.py b/coverage/bytecode.py +index 23c3a6c..541cb3b 100644 +--- a/coverage/bytecode.py ++++ b/coverage/bytecode.py +@@ -6,7 +6,6 @@ + from __future__ import annotations + + import collections +-import dis + from collections.abc import Iterator + from types import CodeType + from typing import Iterable, Optional +@@ -32,7 +31,7 @@ def op_set(*op_names: str) -> set[int]: + + The names might not exist in this version of Python, skip those if not. + """ +- return {op for name in op_names if (op := dis.opmap.get(name))} ++ return set() + + + # Opcodes that are unconditional jumps elsewhere. +@@ -116,6 +115,7 @@ def branch_trails(code: CodeType) -> TBranchTrails: + arc from the original instruction's line to the new source line. + + """ ++ return {} + the_trails: TBranchTrails = collections.defaultdict(lambda: collections.defaultdict(set)) + iwalker = InstructionWalker(code) + for inst in iwalker.walk(follow_jumps=False): +diff --git a/coverage/pytracer.py b/coverage/pytracer.py +index f32015d..deb0006 100644 +--- a/coverage/pytracer.py ++++ b/coverage/pytracer.py +@@ -6,7 +6,6 @@ + from __future__ import annotations + + import atexit +-import dis + import itertools + import sys + import threading +@@ -37,11 +36,11 @@ set_TArc = set[TArc] + + # We need the YIELD_VALUE opcode below, in a comparison-friendly form. + # PYVERSIONS: RESUME is new in Python3.11 +-RESUME = dis.opmap.get("RESUME") +-RETURN_VALUE = dis.opmap["RETURN_VALUE"] ++RESUME = None ++RETURN_VALUE = 1 + if RESUME is None: +- YIELD_VALUE = dis.opmap["YIELD_VALUE"] +- YIELD_FROM = dis.opmap["YIELD_FROM"] ++ YIELD_VALUE = 0 ++ YIELD_FROM = 0 + YIELD_FROM_OFFSET = 0 if env.PYPY else 2 + else: + YIELD_VALUE = YIELD_FROM = YIELD_FROM_OFFSET = -1 +@@ -106,6 +105,7 @@ class PyTracer(Tracer): + # [2] The last line number executed in this frame. + # [3] Boolean: did this frame start a new context? + self.data_stack: list[tuple[TTraceFileData | None, str | None, TLineNo, bool]] = [] ++ self.resumable_frames: set[int] = set() + self.thread: threading.Thread | None = None + self.stopped = False + self._activity = False +@@ -247,10 +247,12 @@ class PyTracer(Tracer): + if RESUME is not None: + # The current opcode is guaranteed to be RESUME. The argument + # determines what kind of resume it is. +- oparg = frame.f_code.co_code[frame.f_lasti + 1] ++ oparg = 0 + real_call = (oparg == 0) # fmt: skip + else: +- real_call = (getattr(frame, "f_lasti", -1) < 0) # fmt: skip ++ frame_id = id(frame) ++ real_call = frame_id not in self.resumable_frames ++ self.resumable_frames.add(frame_id) + if real_call: + self.last_line = -frame.f_code.co_firstlineno + else: +@@ -271,8 +273,9 @@ class PyTracer(Tracer): + if self.trace_arcs and self.cur_file_data: + # Record an arc leaving the function, but beware that a + # "return" event might just mean yielding from a generator. +- code = frame.f_code.co_code +- lasti = frame.f_lasti ++ resumable = frame.f_code.co_flags & (0x20 | 0x80 | 0x100 | 0x200) ++ code = (YIELD_VALUE,) if resumable else (RETURN_VALUE,) ++ lasti = 0 + if RESUME is not None: + if len(code) == lasti + 2: + # A return from the end of a code object is a real return. diff --git a/graalpython/lib-graalpython/patches/coverage-7.11.1.patch b/graalpython/lib-graalpython/patches/coverage-7.11.1.patch new file mode 100644 index 0000000000..63a4dae591 --- /dev/null +++ b/graalpython/lib-graalpython/patches/coverage-7.11.1.patch @@ -0,0 +1,94 @@ +diff --git a/coverage/bytecode.py b/coverage/bytecode.py +index a775591..5d59000 100644 +--- a/coverage/bytecode.py ++++ b/coverage/bytecode.py +@@ -6,7 +6,6 @@ + from __future__ import annotations + + import collections +-import dis + from types import CodeType + from typing import Iterable, Mapping, Optional + +@@ -31,9 +30,7 @@ def op_set(*op_names: str) -> set[int]: + + The names might not exist in this version of Python, skip those if not. + """ +- ops = {op for name in op_names if (op := dis.opmap.get(name))} +- assert ops, f"At least one opcode must exist: {op_names}" +- return ops ++ return set() + + + # Opcodes that are unconditional jumps elsewhere. +@@ -123,6 +120,7 @@ def branch_trails( + arc from the original instruction's line to the new source line. + + """ ++ return {} + the_trails: TBranchTrails = collections.defaultdict(lambda: collections.defaultdict(set)) + iwalker = InstructionWalker(code) + for inst in iwalker.walk(follow_jumps=False): +diff --git a/coverage/pytracer.py b/coverage/pytracer.py +index f32015d..deb0006 100644 +--- a/coverage/pytracer.py ++++ b/coverage/pytracer.py +@@ -6,7 +6,6 @@ + from __future__ import annotations + + import atexit +-import dis + import itertools + import sys + import threading +@@ -37,11 +36,11 @@ set_TArc = set[TArc] + + # We need the YIELD_VALUE opcode below, in a comparison-friendly form. + # PYVERSIONS: RESUME is new in Python3.11 +-RESUME = dis.opmap.get("RESUME") +-RETURN_VALUE = dis.opmap["RETURN_VALUE"] ++RESUME = None ++RETURN_VALUE = 1 + if RESUME is None: +- YIELD_VALUE = dis.opmap["YIELD_VALUE"] +- YIELD_FROM = dis.opmap["YIELD_FROM"] ++ YIELD_VALUE = 0 ++ YIELD_FROM = 0 + YIELD_FROM_OFFSET = 0 if env.PYPY else 2 + else: + YIELD_VALUE = YIELD_FROM = YIELD_FROM_OFFSET = -1 +@@ -106,6 +105,7 @@ class PyTracer(Tracer): + # [2] The last line number executed in this frame. + # [3] Boolean: did this frame start a new context? + self.data_stack: list[tuple[TTraceFileData | None, str | None, TLineNo, bool]] = [] ++ self.resumable_frames: set[int] = set() + self.thread: threading.Thread | None = None + self.stopped = False + self._activity = False +@@ -247,10 +247,12 @@ class PyTracer(Tracer): + if RESUME is not None: + # The current opcode is guaranteed to be RESUME. The argument + # determines what kind of resume it is. +- oparg = frame.f_code.co_code[frame.f_lasti + 1] ++ oparg = 0 + real_call = (oparg == 0) # fmt: skip + else: +- real_call = (getattr(frame, "f_lasti", -1) < 0) # fmt: skip ++ frame_id = id(frame) ++ real_call = frame_id not in self.resumable_frames ++ self.resumable_frames.add(frame_id) + if real_call: + self.last_line = -frame.f_code.co_firstlineno + else: +@@ -271,8 +273,9 @@ class PyTracer(Tracer): + if self.trace_arcs and self.cur_file_data: + # Record an arc leaving the function, but beware that a + # "return" event might just mean yielding from a generator. +- code = frame.f_code.co_code +- lasti = frame.f_lasti ++ resumable = frame.f_code.co_flags & (0x20 | 0x80 | 0x100 | 0x200) ++ code = (YIELD_VALUE,) if resumable else (RETURN_VALUE,) ++ lasti = 0 + if RESUME is not None: + if len(code) == lasti + 2: + # A return from the end of a code object is a real return. diff --git a/graalpython/lib-graalpython/patches/coverage-7.13.1.patch b/graalpython/lib-graalpython/patches/coverage-7.13.1.patch new file mode 100644 index 0000000000..d34497b34c --- /dev/null +++ b/graalpython/lib-graalpython/patches/coverage-7.13.1.patch @@ -0,0 +1,94 @@ +diff --git a/coverage/bytecode.py b/coverage/bytecode.py +index cc179ba..b375154 100644 +--- a/coverage/bytecode.py ++++ b/coverage/bytecode.py +@@ -6,7 +6,6 @@ + from __future__ import annotations + + import collections +-import dis + from collections.abc import Iterable, Mapping + from types import CodeType + from typing import Optional +@@ -32,9 +31,7 @@ def op_set(*op_names: str) -> set[int]: + + The names might not exist in this version of Python, skip those if not. + """ +- ops = {op for name in op_names if (op := dis.opmap.get(name))} +- assert ops, f"At least one opcode must exist: {op_names}" +- return ops ++ return set() + + + # Opcodes that are unconditional jumps elsewhere. +@@ -124,6 +121,7 @@ def branch_trails( + arc from the original instruction's line to the new source line. + + """ ++ return {} + the_trails: TBranchTrails = collections.defaultdict(lambda: collections.defaultdict(set)) + iwalker = InstructionWalker(code) + for inst in iwalker.walk(follow_jumps=False): +diff --git a/coverage/pytracer.py b/coverage/pytracer.py +index c8aac00..081c17e 100644 +--- a/coverage/pytracer.py ++++ b/coverage/pytracer.py +@@ -6,7 +6,6 @@ + from __future__ import annotations + + import atexit +-import dis + import itertools + import sys + import threading +@@ -38,11 +37,11 @@ set_TArc = set[TArc] + + # We need the YIELD_VALUE opcode below, in a comparison-friendly form. + # PYVERSIONS: RESUME is new in Python3.11 +-RESUME = dis.opmap.get("RESUME") +-RETURN_VALUE = dis.opmap["RETURN_VALUE"] ++RESUME = None ++RETURN_VALUE = 1 + if RESUME is None: +- YIELD_VALUE = dis.opmap["YIELD_VALUE"] +- YIELD_FROM = dis.opmap["YIELD_FROM"] ++ YIELD_VALUE = 0 ++ YIELD_FROM = 0 + YIELD_FROM_OFFSET = 0 if env.PYPY else 2 + else: + YIELD_VALUE = YIELD_FROM = YIELD_FROM_OFFSET = -1 +@@ -107,6 +106,7 @@ class PyTracer(Tracer): + # [2] The last line number executed in this frame. + # [3] Boolean: did this frame start a new context? + self.data_stack: list[tuple[TTraceFileData | None, str | None, TLineNo, bool]] = [] ++ self.resumable_frames: set[int] = set() + self.thread: threading.Thread | None = None + self.stopped = False + self._activity = False +@@ -248,10 +248,12 @@ class PyTracer(Tracer): + if RESUME is not None: + # The current opcode is guaranteed to be RESUME. The argument + # determines what kind of resume it is. +- oparg = frame.f_code.co_code[frame.f_lasti + 1] ++ oparg = 0 + real_call = (oparg == 0) # fmt: skip + else: +- real_call = (getattr(frame, "f_lasti", -1) < 0) # fmt: skip ++ frame_id = id(frame) ++ real_call = frame_id not in self.resumable_frames ++ self.resumable_frames.add(frame_id) + if real_call: + self.last_line = -frame.f_code.co_firstlineno + else: +@@ -272,8 +274,9 @@ class PyTracer(Tracer): + if self.trace_arcs and self.cur_file_data: + # Record an arc leaving the function, but beware that a + # "return" event might just mean yielding from a generator. +- code = frame.f_code.co_code +- lasti = frame.f_lasti ++ resumable = frame.f_code.co_flags & (0x20 | 0x80 | 0x100 | 0x200) ++ code = (YIELD_VALUE,) if resumable else (RETURN_VALUE,) ++ lasti = 0 + if RESUME is not None: + if len(code) == lasti + 2: + # A return from the end of a code object is a real return. diff --git a/graalpython/lib-graalpython/patches/coverage-7.15.1.patch b/graalpython/lib-graalpython/patches/coverage-7.15.1.patch new file mode 100644 index 0000000000..e920205ae0 --- /dev/null +++ b/graalpython/lib-graalpython/patches/coverage-7.15.1.patch @@ -0,0 +1,105 @@ +diff --git a/coverage/bytecode.py b/coverage/bytecode.py +index 0546b97..1aabf5a 100644 +--- a/coverage/bytecode.py ++++ b/coverage/bytecode.py +@@ -5,7 +5,6 @@ + + from __future__ import annotations + +-import dis + from collections.abc import Iterable, Mapping + from types import CodeType + +@@ -88,9 +87,7 @@ def op_set(*op_names: str) -> set[int]: + + The names might not exist in this version of Python, skip those if not. + """ +- ops = {op for name in op_names if (op := dis.opmap.get(name))} +- assert ops, f"At least one opcode must exist: {op_names}" +- return ops ++ return set() + + + # Opcodes that are unconditional jumps elsewhere. +@@ -109,14 +106,14 @@ RETURNS = op_set( + + # CACHE doesn't exist in Python 3.10, but the branch resolver is only used + # on 3.14+, so a placeholder value is fine. +-_CACHE = dis.opmap.get("CACHE", -1) +-_EXTENDED_ARG = dis.opmap["EXTENDED_ARG"] ++_CACHE = -1 ++_EXTENDED_ARG = -1 + + # All opcodes with a jump target. +-JUMPS = set(dis.hasjrel) | set(dis.hasjabs) ++JUMPS = set() + + # Opcodes that jump backwards. +-BACKWARD_JUMPS = {op for op in JUMPS if "JUMP_BACKWARD" in dis.opname[op]} ++BACKWARD_JUMPS = set() + + + class BranchArcResolver: +diff --git a/coverage/pytracer.py b/coverage/pytracer.py +index c8aac00..081c17e 100644 +--- a/coverage/pytracer.py ++++ b/coverage/pytracer.py +@@ -6,7 +6,6 @@ + from __future__ import annotations + + import atexit +-import dis + import itertools + import sys + import threading +@@ -38,11 +37,11 @@ set_TArc = set[TArc] + + # We need the YIELD_VALUE opcode below, in a comparison-friendly form. + # PYVERSIONS: RESUME is new in Python3.11 +-RESUME = dis.opmap.get("RESUME") +-RETURN_VALUE = dis.opmap["RETURN_VALUE"] ++RESUME = None ++RETURN_VALUE = 1 + if RESUME is None: +- YIELD_VALUE = dis.opmap["YIELD_VALUE"] +- YIELD_FROM = dis.opmap["YIELD_FROM"] ++ YIELD_VALUE = 0 ++ YIELD_FROM = 0 + YIELD_FROM_OFFSET = 0 if env.PYPY else 2 + else: + YIELD_VALUE = YIELD_FROM = YIELD_FROM_OFFSET = -1 +@@ -107,6 +106,7 @@ class PyTracer(Tracer): + # [2] The last line number executed in this frame. + # [3] Boolean: did this frame start a new context? + self.data_stack: list[tuple[TTraceFileData | None, str | None, TLineNo, bool]] = [] ++ self.resumable_frames: set[int] = set() + self.thread: threading.Thread | None = None + self.stopped = False + self._activity = False +@@ -248,10 +248,12 @@ class PyTracer(Tracer): + if RESUME is not None: + # The current opcode is guaranteed to be RESUME. The argument + # determines what kind of resume it is. +- oparg = frame.f_code.co_code[frame.f_lasti + 1] ++ oparg = 0 + real_call = (oparg == 0) # fmt: skip + else: +- real_call = (getattr(frame, "f_lasti", -1) < 0) # fmt: skip ++ frame_id = id(frame) ++ real_call = frame_id not in self.resumable_frames ++ self.resumable_frames.add(frame_id) + if real_call: + self.last_line = -frame.f_code.co_firstlineno + else: +@@ -272,8 +274,9 @@ class PyTracer(Tracer): + if self.trace_arcs and self.cur_file_data: + # Record an arc leaving the function, but beware that a + # "return" event might just mean yielding from a generator. +- code = frame.f_code.co_code +- lasti = frame.f_lasti ++ resumable = frame.f_code.co_flags & (0x20 | 0x80 | 0x100 | 0x200) ++ code = (YIELD_VALUE,) if resumable else (RETURN_VALUE,) ++ lasti = 0 + if RESUME is not None: + if len(code) == lasti + 2: + # A return from the end of a code object is a real return. diff --git a/graalpython/lib-graalpython/patches/coverage-7.16.2.patch b/graalpython/lib-graalpython/patches/coverage-7.16.2.patch new file mode 100644 index 0000000000..f9cc71c2e9 --- /dev/null +++ b/graalpython/lib-graalpython/patches/coverage-7.16.2.patch @@ -0,0 +1,109 @@ +diff --git a/coverage/bytecode.py b/coverage/bytecode.py +index ea0c527..38858e7 100644 +--- a/coverage/bytecode.py ++++ b/coverage/bytecode.py +@@ -5,7 +5,6 @@ + + from __future__ import annotations + +-import dis + from collections.abc import Iterable, Mapping + from types import CodeType + +@@ -88,9 +87,7 @@ def op_set(*op_names: str) -> set[int]: + + The names might not exist in this version of Python, skip those if not. + """ +- ops = {op for name in op_names if (op := dis.opmap.get(name))} +- assert ops, f"At least one opcode must exist: {op_names}" +- return ops ++ return set() + + + # Opcodes that are unconditional jumps elsewhere. +@@ -113,14 +110,14 @@ NO_FALL_THROUGH = op_set("RERAISE") + + # CACHE doesn't exist in Python 3.10, but the branch resolver is only used + # on 3.14+, so a placeholder value is fine. +-_CACHE = dis.opmap.get("CACHE", -1) +-_EXTENDED_ARG = dis.opmap["EXTENDED_ARG"] ++_CACHE = -1 ++_EXTENDED_ARG = -1 + + # All opcodes with a jump target. +-JUMPS = set(dis.hasjrel) | set(dis.hasjabs) ++JUMPS = set() + + # Opcodes that jump backwards. +-BACKWARD_JUMPS = {op for op in JUMPS if "JUMP_BACKWARD" in dis.opname[op]} ++BACKWARD_JUMPS = set() + + + class BranchArcResolver: +diff --git a/coverage/pytracer.py b/coverage/pytracer.py +index 4a8a963..c991f02 100644 +--- a/coverage/pytracer.py ++++ b/coverage/pytracer.py +@@ -6,7 +6,6 @@ + from __future__ import annotations + + import atexit +-import dis + import itertools + import sys + import threading +@@ -38,14 +37,14 @@ set_TArc = set[TArc] + + # We need the YIELD_VALUE opcode below, in a comparison-friendly form. + # PYVERSIONS: RESUME is new in Python3.11 +-RESUME = dis.opmap.get("RESUME") +-RETURN_VALUE = dis.opmap["RETURN_VALUE"] ++RESUME = None ++RETURN_VALUE = 1 + # PyPy attributes the exception-handler PUSH_EXC_INFO to the try/else line. + # That instruction is not the else body. Skip it so the else line stays missing. +-PUSH_EXC_INFO = dis.opmap.get("PUSH_EXC_INFO") ++PUSH_EXC_INFO = None + if RESUME is None: +- YIELD_VALUE = dis.opmap["YIELD_VALUE"] +- YIELD_FROM = dis.opmap["YIELD_FROM"] ++ YIELD_VALUE = 0 ++ YIELD_FROM = 0 + YIELD_FROM_OFFSET = 0 if env.PYPY else 2 + else: + YIELD_VALUE = YIELD_FROM = YIELD_FROM_OFFSET = -1 +@@ -110,6 +109,7 @@ class PyTracer(Tracer): + # [2] The last line number executed in this frame. + # [3] Boolean: did this frame start a new context? + self.data_stack: list[tuple[TTraceFileData | None, str | None, TLineNo, bool]] = [] ++ self.resumable_frames: set[int] = set() + self.thread: threading.Thread | None = None + self.stopped = False + self._activity = False +@@ -248,10 +248,12 @@ class PyTracer(Tracer): + if RESUME is not None: + # The current opcode is guaranteed to be RESUME. The argument + # determines what kind of resume it is. +- oparg = frame.f_code.co_code[frame.f_lasti + 1] ++ oparg = 0 + real_call = (oparg == 0) # fmt: skip + else: +- real_call = (getattr(frame, "f_lasti", -1) < 0) # fmt: skip ++ frame_id = id(frame) ++ real_call = frame_id not in self.resumable_frames ++ self.resumable_frames.add(frame_id) + if real_call: + self.last_line = -frame.f_code.co_firstlineno + else: +@@ -281,8 +283,9 @@ class PyTracer(Tracer): + if self.trace_arcs and self.cur_file_data: + # Record an arc leaving the function, but beware that a + # "return" event might just mean yielding from a generator. +- code = frame.f_code.co_code +- lasti = frame.f_lasti ++ resumable = frame.f_code.co_flags & (0x20 | 0x80 | 0x100 | 0x200) ++ code = (YIELD_VALUE,) if resumable else (RETURN_VALUE,) ++ lasti = 0 + if RESUME is not None: + if len(code) == lasti + 2: + # A return from the end of a code object is a real return. diff --git a/graalpython/lib-graalpython/patches/coverage-7.17.0.patch b/graalpython/lib-graalpython/patches/coverage-7.17.0.patch new file mode 100644 index 0000000000..7199b2f62d --- /dev/null +++ b/graalpython/lib-graalpython/patches/coverage-7.17.0.patch @@ -0,0 +1,109 @@ +diff --git a/coverage/bytecode.py b/coverage/bytecode.py +index f795257..b7b6cc3 100644 +--- a/coverage/bytecode.py ++++ b/coverage/bytecode.py +@@ -5,7 +5,6 @@ + + from __future__ import annotations + +-import dis + from collections.abc import Iterable, Mapping + from types import CodeType + +@@ -88,9 +87,7 @@ def op_set(*op_names: str) -> set[int]: + + The names might not exist in this version of Python, skip those if not. + """ +- ops = {op for name in op_names if (op := dis.opmap.get(name))} +- assert ops, f"At least one opcode must exist: {op_names}" +- return ops ++ return set() + + + # Opcodes that are unconditional jumps elsewhere. +@@ -113,14 +110,14 @@ NO_FALL_THROUGH = op_set("RERAISE") + + # CACHE doesn't exist in PyPy, but the branch resolver is only used + # by sysmond, so a placeholder value is fine. +-_CACHE = dis.opmap.get("CACHE", -1) +-_EXTENDED_ARG = dis.opmap["EXTENDED_ARG"] ++_CACHE = -1 ++_EXTENDED_ARG = -1 + + # All opcodes with a jump target. +-JUMPS = set(dis.hasjrel) | set(dis.hasjabs) ++JUMPS = set() + + # Opcodes that jump backwards. +-BACKWARD_JUMPS = {op for op in JUMPS if "JUMP_BACKWARD" in dis.opname[op]} ++BACKWARD_JUMPS = set() + + + class BranchArcResolver: +diff --git a/coverage/pytracer.py b/coverage/pytracer.py +index 844b82a..ade96a9 100644 +--- a/coverage/pytracer.py ++++ b/coverage/pytracer.py +@@ -6,7 +6,6 @@ + from __future__ import annotations + + import atexit +-import dis + import itertools + import sys + import threading +@@ -38,14 +37,14 @@ set_TArc = set[TArc] + + # We need the YIELD_VALUE opcode below, in a comparison-friendly form. + # PYVERSIONS: RESUME is new in CPython 3.11 +-RESUME = dis.opmap.get("RESUME") +-RETURN_VALUE = dis.opmap["RETURN_VALUE"] ++RESUME = None ++RETURN_VALUE = 1 + # PyPy attributes the exception-handler PUSH_EXC_INFO to the try/else line. + # That instruction is not the else body. Skip it so the else line stays missing. +-PUSH_EXC_INFO = dis.opmap.get("PUSH_EXC_INFO") ++PUSH_EXC_INFO = None + if RESUME is None: +- YIELD_VALUE = dis.opmap["YIELD_VALUE"] +- YIELD_FROM = dis.opmap["YIELD_FROM"] ++ YIELD_VALUE = 0 ++ YIELD_FROM = 0 + YIELD_FROM_OFFSET = 0 if env.PYPY else 2 + else: + YIELD_VALUE = YIELD_FROM = YIELD_FROM_OFFSET = -1 +@@ -110,6 +109,7 @@ class PyTracer(Tracer): + # [2] The last line number executed in this frame. + # [3] Boolean: did this frame start a new context? + self.data_stack: list[tuple[TTraceFileData | None, str | None, TLineNo, bool]] = [] ++ self.resumable_frames: set[int] = set() + self.thread: threading.Thread | None = None + self.stopped = False + self._activity = False +@@ -248,10 +248,12 @@ class PyTracer(Tracer): + if RESUME is not None: + # The current opcode is guaranteed to be RESUME. The argument + # determines what kind of resume it is. +- oparg = frame.f_code.co_code[frame.f_lasti + 1] ++ oparg = 0 + real_call = (oparg == 0) # fmt: skip + else: +- real_call = (getattr(frame, "f_lasti", -1) < 0) # fmt: skip ++ frame_id = id(frame) ++ real_call = frame_id not in self.resumable_frames ++ self.resumable_frames.add(frame_id) + if real_call: + self.last_line = -frame.f_code.co_firstlineno + else: +@@ -281,8 +283,9 @@ class PyTracer(Tracer): + if self.trace_arcs and self.cur_file_data: + # Record an arc leaving the function, but beware that a + # "return" event might just mean yielding from a generator. +- code = frame.f_code.co_code +- lasti = frame.f_lasti ++ resumable = frame.f_code.co_flags & (0x20 | 0x80 | 0x100 | 0x200) ++ code = (YIELD_VALUE,) if resumable else (RETURN_VALUE,) ++ lasti = 0 + if RESUME is not None: + if len(code) == lasti + 2: + # A return from the end of a code object is a real return. diff --git a/graalpython/lib-graalpython/patches/joblib-1.5.0.patch b/graalpython/lib-graalpython/patches/joblib-1.5.0.patch new file mode 100644 index 0000000000..1a2812ffee --- /dev/null +++ b/graalpython/lib-graalpython/patches/joblib-1.5.0.patch @@ -0,0 +1,60 @@ +diff --git a/joblib/_parallel_backends.py b/joblib/_parallel_backends.py +index 53114a8..d714666 100644 +--- a/joblib/_parallel_backends.py ++++ b/joblib/_parallel_backends.py +@@ -569,6 +569,10 @@ class MultiprocessingBackend(PoolManagerMixin, AutoBatchingMixin, ParallelBacken + ) + return 1 + ++ # GraalPy change: ++ if n_jobs < 0: ++ return max(__graalpython__.get_max_process_count() + 1 + n_jobs, 1) ++ + return super(MultiprocessingBackend, self).effective_n_jobs(n_jobs) + + def configure( +@@ -685,7 +689,8 @@ class LokyBackend(AutoBatchingMixin, ParallelBackendBase): + ) + return 1 + elif n_jobs < 0: +- n_jobs = max(cpu_count() + 1 + n_jobs, 1) ++ # GraalPy change: ++ n_jobs = max(__graalpython__.get_max_process_count() + 1 + n_jobs, 1) + return n_jobs + + def submit(self, func, callback=None): +diff --git a/joblib/externals/cloudpickle/cloudpickle.py b/joblib/externals/cloudpickle/cloudpickle.py +index 4d532e5..571c5a3 100644 +--- a/joblib/externals/cloudpickle/cloudpickle.py ++++ b/joblib/externals/cloudpickle/cloudpickle.py +@@ -410,10 +410,8 @@ def _builtin_type(name): + + def _walk_global_ops(code): + """Yield referenced name for global-referencing instructions in code.""" +- for instr in dis.get_instructions(code): +- op = instr.opcode +- if op in GLOBAL_OPS: +- yield instr.argval ++ # GraalPy change: we do not support dis. ++ yield from code.co_names + + + def _extract_class_dict(cls): +diff --git a/joblib/externals/loky/backend/fork_exec.py b/joblib/externals/loky/backend/fork_exec.py +index f5b7ca6..62fda5b 100644 +--- a/joblib/externals/loky/backend/fork_exec.py ++++ b/joblib/externals/loky/backend/fork_exec.py +@@ -10,6 +10,13 @@ import subprocess + + + def fork_exec(cmd, keep_fds, env=None): ++ # GraalPy change: use the supported subprocess API. ++ if sys.implementation.name == "graalpy": ++ child_env = {**os.environ, **(env or {})} ++ return subprocess.Popen( ++ cmd, executable=sys.executable, pass_fds=keep_fds, env=child_env ++ ).pid ++ + import _posixsubprocess + + # Encoded command args as bytes: diff --git a/graalpython/lib-graalpython/patches/joblib-1.6.0.patch b/graalpython/lib-graalpython/patches/joblib-1.6.0.patch new file mode 100644 index 0000000000..29606894a6 --- /dev/null +++ b/graalpython/lib-graalpython/patches/joblib-1.6.0.patch @@ -0,0 +1,43 @@ +diff --git a/joblib/_parallel_backends.py b/joblib/_parallel_backends.py +index 53114a8..d714666 100644 +--- a/joblib/_parallel_backends.py ++++ b/joblib/_parallel_backends.py +@@ -569,6 +569,10 @@ class MultiprocessingBackend(PoolManagerMixin, AutoBatchingMixin, ParallelBacken + ) + return 1 + ++ # GraalPy change: ++ if n_jobs < 0: ++ return max(__graalpython__.get_max_process_count() + 1 + n_jobs, 1) ++ + return super(MultiprocessingBackend, self).effective_n_jobs(n_jobs) + + def configure( +@@ -685,7 +689,8 @@ class LokyBackend(AutoBatchingMixin, ParallelBackendBase): + ) + return 1 + elif n_jobs < 0: +- n_jobs = max(cpu_count() + 1 + n_jobs, 1) ++ # GraalPy change: ++ n_jobs = max(__graalpython__.get_max_process_count() + 1 + n_jobs, 1) + return n_jobs + + def submit(self, func, callback=None): +diff --git a/joblib/externals/loky/backend/fork_exec.py b/joblib/externals/loky/backend/fork_exec.py +index f5b7ca6..62fda5b 100644 +--- a/joblib/externals/loky/backend/fork_exec.py ++++ b/joblib/externals/loky/backend/fork_exec.py +@@ -10,6 +10,13 @@ import subprocess + + + def fork_exec(cmd, keep_fds, env=None): ++ # GraalPy change: use the supported subprocess API. ++ if sys.implementation.name == "graalpy": ++ child_env = {**os.environ, **(env or {})} ++ return subprocess.Popen( ++ cmd, executable=sys.executable, pass_fds=keep_fds, env=child_env ++ ).pid ++ + import _posixsubprocess + + # Encoded command args as bytes: diff --git a/graalpython/lib-graalpython/patches/metadata.toml b/graalpython/lib-graalpython/patches/metadata.toml index c0475c73ad..e425b66274 100644 --- a/graalpython/lib-graalpython/patches/metadata.toml +++ b/graalpython/lib-graalpython/patches/metadata.toml @@ -29,10 +29,49 @@ patch = 'cloudpickle-2.2.1.patch' license = 'BSD-3-Clause' [[coverage.rules]] -version = '== 7.10.0' +version = '>= 7.10.0, < 7.10.2' patch = 'coverage-7.10.0.patch' license = 'Apache-2.0' -dist-type = 'wheel' + +[[coverage.rules]] +version = '>= 7.10.2, < 7.10.3' +patch = 'coverage-7.10.2.patch' +license = 'Apache-2.0' + +[[coverage.rules]] +version = '>= 7.10.3, < 7.10.5' +patch = 'coverage-7.10.3.patch' +license = 'Apache-2.0' + +[[coverage.rules]] +version = '>= 7.10.5, < 7.11.1' +patch = 'coverage-7.10.5.patch' +license = 'Apache-2.0' + +[[coverage.rules]] +version = '>= 7.11.1, < 7.13.1' +patch = 'coverage-7.11.1.patch' +license = 'Apache-2.0' + +[[coverage.rules]] +version = '>= 7.13.1, < 7.15.1' +patch = 'coverage-7.13.1.patch' +license = 'Apache-2.0' + +[[coverage.rules]] +version = '>= 7.15.1, < 7.16.2' +patch = 'coverage-7.15.1.patch' +license = 'Apache-2.0' + +[[coverage.rules]] +version = '>= 7.16.2, < 7.17.0' +patch = 'coverage-7.16.2.patch' +license = 'Apache-2.0' + +[[coverage.rules]] +version = '>= 7.17.0, < 7.17.3' +patch = 'coverage-7.17.0.patch' +license = 'Apache-2.0' [[cramjam.rules]] version = '== 2.7.0' @@ -237,6 +276,16 @@ license = 'Apache-2.0' version = '0.4.13' url = 'https://github.com/google/jax/archive/refs/tags/jaxlib-v0.4.13.tar.gz' +[[joblib.rules]] +version = '== 1.6.*' +patch = 'joblib-1.6.0.patch' +license = 'BSD-3-Clause' + +[[joblib.rules]] +version = '== 1.5.*' +patch = 'joblib-1.5.0.patch' +license = 'BSD-3-Clause' + [[joblib.rules]] version = '==1.4.*' patch = 'joblib-1.4.0.patch' @@ -339,11 +388,17 @@ patch = 'numba-0.65.1.patch' license = 'BSD-2-Clause' [[numpy.rules]] -version = '>= 2.4, < 2.4.5' +version = '>= 2.4, < 2.5' patch = 'numpy-2.4.4.patch' license = 'BSD-3-Clause' dist-type = 'sdist' +[[numpy.rules]] +version = '>= 2.5, < 2.5.5' +patch = 'numpy-2.5.4.patch' +license = 'BSD-3-Clause' +dist-type = 'sdist' + [[numpy.rules]] version = '>= 2.0.0rc1, < 2.4' patch = 'numpy-2.0.0.patch' @@ -402,6 +457,40 @@ url = 'https://github.com/microsoft/onnxruntime/archive/refs/tags/v1.25.0.tar.gz version = '1.25.1' url = 'https://github.com/microsoft/onnxruntime/archive/refs/tags/v1.25.1.tar.gz' +[[onnxruntime.rules]] +version = '>= 1.26, < 1.32' +patch = 'onnxruntime-1.31.1.patch' +license = 'MIT' +dist-type = 'sdist' + +[[onnxruntime.add-sources]] +version = '1.26.0' +url = 'https://github.com/microsoft/onnxruntime/archive/refs/tags/v1.26.0.tar.gz' + +[[onnxruntime.add-sources]] +version = '1.27.0' +url = 'https://github.com/microsoft/onnxruntime/archive/refs/tags/v1.27.0.tar.gz' + +[[onnxruntime.add-sources]] +version = '1.28.0' +url = 'https://github.com/microsoft/onnxruntime/archive/refs/tags/v1.28.0.tar.gz' + +[[onnxruntime.add-sources]] +version = '1.29.0' +url = 'https://github.com/microsoft/onnxruntime/archive/refs/tags/v1.29.0.tar.gz' + +[[onnxruntime.add-sources]] +version = '1.30.0' +url = 'https://github.com/microsoft/onnxruntime/archive/refs/tags/v1.30.0.tar.gz' + +[[onnxruntime.add-sources]] +version = '1.31.0' +url = 'https://github.com/microsoft/onnxruntime/archive/refs/tags/v1.31.0.tar.gz' + +[[onnxruntime.add-sources]] +version = '1.31.1' +url = 'https://github.com/microsoft/onnxruntime/archive/refs/tags/v1.31.1.tar.gz' + [[optree.rules]] patch = 'optree.patch' license = 'Apache-2.0' @@ -419,10 +508,21 @@ license = 'Apache-2.0 OR MIT' [[orjson.rules]] version = '== 3.11.9' patch = 'orjson-3.11.9.patch' -license = 'Apache-2.0 OR MIT' +license = 'MPL-2.0 AND (Apache-2.0 OR MIT)' + +[[orjson.rules]] +version = '== 3.12.0' +patch = 'orjson-3.12.0.patch' +license = 'MPL-2.0 AND (Apache-2.0 OR MIT)' + +[[orjson.rules]] +version = '== 3.13.0' +patch = 'orjson-3.13.0.patch' +license = 'MPL-2.0 AND (Apache-2.0 OR MIT)' [[ormsgpack.rules]] version = '>= 1.8.0, <= 1.9.1' +install-priority = 0 # Upstream supports GraalPy in newer release patch = 'ormsgpack-1.8.0-1.9.1.patch' license = 'Apache-2.0 OR MIT' @@ -443,7 +543,7 @@ patch = 'packaging.patch' license = 'Apache-2.0 OR BSD-2-Clause' [[pandas.rules]] -version = '>= 3.0, < 3.0.6' +version = '>= 3.0, < 3.2' patch = 'pandas-3.0.2.patch' license = 'BSD-3-Clause' dist-type = 'sdist' @@ -496,7 +596,12 @@ license = 'BSD-3-Clause' subdir = 'src' [[pyarrow.rules]] -version = '== 24.0.0' +version = '>= 26.0.0, <= 26.0.1' +patch = 'pyarrow-26.0.0.patch' +license = 'Apache-2.0' + +[[pyarrow.rules]] +version = '>= 24.0.0, < 26.0.0' patch = 'pyarrow-24.0.0.patch' license = 'Apache-2.0' @@ -596,22 +701,31 @@ note = "Building PyMuPDF needs a CPython installation besides GraalPy on the PAT license = 'UPL' [[pyperf.rules]] -version = '== 2.3.0' +version = '>= 1.6.1, < 2.3.1' install-priority = 0 patch = 'pyperf-2.3.0.patch' license = 'MIT' [[pyperf.rules]] -version = '== 2.3.1' +version = '>= 2.3.1, < 2.5' install-priority = 0 patch = 'pyperf-2.3.1.patch' license = 'MIT' [[pyperf.rules]] -version = '== 2.10.0' +version = '>= 2.5, < 2.8' +note = 'GraalPy JIT detection is supported upstream and warmups default to automatic calibration.' + +[[pyperf.rules]] +version = '>= 2.8, < 2.11' patch = 'pyperf-2.10.0.patch' license = 'MIT' +[[pyperf.rules]] +version = '== 2.11.*' +patch = 'pyperf-2.11.0.patch' +license = 'MIT' + [[pyperformance.rules]] version = '== 1.0.5' patch = 'pyperformance-1.0.5.patch' @@ -644,7 +758,7 @@ patch = 'pythran-0.16.patch' license = 'BSD-3-Clause' [[pythran.rules]] -version = '== 0.18.1' +version = '>= 0.18.1, <= 0.19.0' patch = 'pythran-0.18.1.patch' license = 'BSD-3-Clause' @@ -654,10 +768,15 @@ patch = 'rapidfuzz.patch' license = 'MIT' [[rapidfuzz.rules]] -version = '>= 3.10.0, <= 3.12.2' +version = '>= 3.10.0, < 3.14.1' patch = 'rapidfuzz-3.10.patch' license = 'MIT' +[[rapidfuzz.rules]] +version = '>= 3.14.1, <= 3.14.6' +patch = 'rapidfuzz-3.14.1.patch' +license = 'MIT' + [[levenshtein.add-sources]] version = '0.26.1' url = 'https://github.com/rapidfuzz/Levenshtein/archive/refs/tags/v0.26.1.zip' @@ -775,6 +894,11 @@ subdir = 'src' version = '>= 2, < 2.0.3' install-priority = 0 +[[uvloop.rules]] +version = '>= 0.23.0, < 0.24' +patch = 'uvloop-0.23.0.patch' +license = 'MIT' + [[uvloop.rules]] version = '== 0.22.1' patch = 'uvloop-0.22.1.patch' diff --git a/graalpython/lib-graalpython/patches/numpy-2.5.4.patch b/graalpython/lib-graalpython/patches/numpy-2.5.4.patch new file mode 100644 index 0000000000..69d54c1ded --- /dev/null +++ b/graalpython/lib-graalpython/patches/numpy-2.5.4.patch @@ -0,0 +1,163 @@ +diff --git a/numpy/_core/include/numpy/ndarrayobject.h b/numpy/_core/include/numpy/ndarrayobject.h +index 405b9d8..2b3a9d1 100644 +--- a/numpy/_core/include/numpy/ndarrayobject.h ++++ b/numpy/_core/include/numpy/ndarrayobject.h +@@ -211,6 +211,12 @@ NPY_TITLE_KEY_check(PyObject *key, PyObject *value) + if (key == title) { + return 1; + } ++#ifdef GRAALVM_PYTHON ++ /* GraalPy dictionary keys do not always preserve object identity. */ ++ if (PyUnicode_Check(title) && PyUnicode_Check(key)) { ++ return PyUnicode_Compare(title, key) == 0 ? 1 : 0; ++ } ++#endif + return 0; + } + +diff --git a/numpy/_core/meson.build b/numpy/_core/meson.build +index b4cb250..8476397 100644 +--- a/numpy/_core/meson.build ++++ b/numpy/_core/meson.build +@@ -914,6 +914,9 @@ endforeach + + # Build npysort dispatch-able sources + # ----------------------------------- ++# GraalPy change: GCC 12 can report a false positive maybe-uninitialized ++# warning in AVX512 intrinsics used by x86-simd-sort. Some CI builds use Werror. ++combined_cpp_args = cpp_args_common + max_opt + cpp.get_supported_arguments('-Wno-error=maybe-uninitialized') + foreach gen_mtargets : [ + [ + 'x86_simd_argsort.dispatch.h', +@@ -957,7 +960,7 @@ foreach gen_mtargets : [ + prefix: 'NPY_', + dependencies: [py_dep, np_core_dep, omp_dep], + c_args: c_args_common + max_opt, +- cpp_args: cpp_args_common + max_opt, ++ cpp_args: combined_cpp_args, + include_directories: [ + 'include', + 'src/common', +diff --git a/numpy/_core/src/multiarray/compiled_base.c b/numpy/_core/src/multiarray/compiled_base.c +index dd28d16..6e66d45 100644 +--- a/numpy/_core/src/multiarray/compiled_base.c ++++ b/numpy/_core/src/multiarray/compiled_base.c +@@ -1504,6 +1504,7 @@ fail: + NPY_NO_EXPORT PyObject * + arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *const *args, Py_ssize_t len_args) + { ++#if 0 // GraalPy change + PyObject *obj; + PyObject *str; + const char *docstr; +@@ -1607,6 +1608,7 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *const *args, Py_ssize_t + } + + #undef _ADDDOC ++#endif // GraalPy change + + Py_RETURN_NONE; + } +diff --git a/numpy/_core/src/multiarray/shape.c b/numpy/_core/src/multiarray/shape.c +index bbdc207..d78b892 100644 +--- a/numpy/_core/src/multiarray/shape.c ++++ b/numpy/_core/src/multiarray/shape.c +@@ -88,6 +88,12 @@ PyArray_Resize_int(PyArrayObject *self, PyArray_Dims *newshape, int refcheck) + return -1; + } + if (refcheck) { ++#ifdef GRAALVM_PYTHON ++ PyErr_SetString(PyExc_ValueError, ++ "cannot resize an array with refcheck=True on GraalPy.\n" ++ "Use the np.resize function or refcheck=False"); ++ return -1; ++#else + #if PY_VERSION_HEX >= 0x030E00B0 + // Python 3.14 changed reference counting semantics for function- + // local variables. There is no way to tell if the calling function +@@ -112,6 +118,7 @@ PyArray_Resize_int(PyArrayObject *self, PyArray_Dims *newshape, int refcheck) + PyErr_SetString(PyExc_ValueError, msg); + return -1; + } ++#endif // GraalPy change + } + /* Reallocate space if needed - allocating 0 is forbidden */ + PyObject *handler = PyArray_HANDLER(self); +diff --git a/numpy/_core/src/multiarray/stringdtype/dtype.c b/numpy/_core/src/multiarray/stringdtype/dtype.c +index e66f969..7cdec71 100644 +--- a/numpy/_core/src/multiarray/stringdtype/dtype.c ++++ b/numpy/_core/src/multiarray/stringdtype/dtype.c +@@ -1099,7 +1099,7 @@ init_string_dtype(void) + }; + + /* Loaded dynamically, so needs to be set here: */ +- Py_TYPE(((PyObject *)&PyArray_StringDType)) = &PyArrayDTypeMeta_Type; ++ Py_SET_TYPE(((PyObject *)&PyArray_StringDType), &PyArrayDTypeMeta_Type); + ((PyTypeObject *)&PyArray_StringDType)->tp_base = &PyArrayDescr_Type; + if (PyType_Ready((PyTypeObject *)&PyArray_StringDType) < 0) { + return -1; +diff --git a/numpy/_core/src/multiarray/temp_elide.c b/numpy/_core/src/multiarray/temp_elide.c +index 6a26ee1..1fb4f64 100644 +--- a/numpy/_core/src/multiarray/temp_elide.c ++++ b/numpy/_core/src/multiarray/temp_elide.c +@@ -59,7 +59,7 @@ + * supported too by using the appropriate Windows APIs. + */ + +-#if defined HAVE_BACKTRACE && defined HAVE_DLFCN_H ++#if defined HAVE_BACKTRACE && defined HAVE_DLFCN_H && !defined(GRAALVM_PYTHON) + + #include + +diff --git a/numpy/_core/src/npymath/ieee754.c.src b/numpy/_core/src/npymath/ieee754.c.src +index 8fccc9a..93ee333 100644 +--- a/numpy/_core/src/npymath/ieee754.c.src ++++ b/numpy/_core/src/npymath/ieee754.c.src +@@ -8,6 +8,9 @@ + #include "npy_math_private.h" + #include "numpy/utils.h" + ++// GraalPy change ++#include ++ + /* + The below code is provided for compilers which do not yet provide C11 compatibility (gcc 4.5 and older) + */ +@@ -362,6 +365,11 @@ int npy_get_floatstatus_barrier(char* param) + * By using a volatile, the compiler cannot reorder this call + */ + if (param != NULL) { ++ // GraalPy change: the pointer needs to be dereferenced to establish ++ // a data dependency to to ensure the compiler won't reorder the call ++ if (points_to_py_handle_space(param)) { ++ param = (char*)pointer_to_stub(param); ++ } + volatile char NPY_UNUSED(c) = *(char*)param; + } + +diff --git a/numpy/_core/src/npymath/ieee754.cpp b/numpy/_core/src/npymath/ieee754.cpp +index 1c59bf3..0d8cb63 100644 +--- a/numpy/_core/src/npymath/ieee754.cpp ++++ b/numpy/_core/src/npymath/ieee754.cpp +@@ -9,6 +9,9 @@ + #include "npy_math_common.h" + #include "npy_math_private.h" + ++// GraalPy change ++#include ++ + /* + The below code is provided for compilers which do not yet provide C11 + compatibility (gcc 4.5 and older) +@@ -428,6 +431,11 @@ npy_get_floatstatus_barrier(char *param) + * By using a volatile, the compiler cannot reorder this call + */ + if (param != NULL) { ++ // GraalPy change: the pointer needs to be dereferenced to establish ++ // a data dependency to to ensure the compiler won't reorder the call ++ if (points_to_py_handle_space(param)) { ++ param = (char*)pointer_to_stub(param); ++ } + volatile char NPY_UNUSED(c) = *(char *)param; + } + diff --git a/graalpython/lib-graalpython/patches/onnxruntime-1.31.1.patch b/graalpython/lib-graalpython/patches/onnxruntime-1.31.1.patch new file mode 100644 index 0000000000..41570ed9ee --- /dev/null +++ b/graalpython/lib-graalpython/patches/onnxruntime-1.31.1.patch @@ -0,0 +1,77 @@ +diff --git a/onnxruntime_build_backend.py b/onnxruntime_build_backend.py +new file mode 100644 +index 0000000..289fa45 +--- /dev/null ++++ b/onnxruntime_build_backend.py +@@ -0,0 +1,57 @@ ++import os ++import re ++import sys ++import tarfile ++import subprocess ++import shutil ++from pathlib import Path ++ ++VERSION = Path("VERSION_NUMBER").read_text().strip() ++ ++def build_sdist(sdist_directory, config_settings=None): ++ nv = f'onnxruntime-{VERSION}' ++ archive_path = Path(sdist_directory) / f'{nv}.tar.gz' ++ ++ def tarfilter(info): ++ if re.match(r'\./(?:.git|venv|[^-/]+-venv|dist)', info.name): ++ return None ++ info.name = f'./{nv}/{info.name}' ++ return info ++ ++ with tarfile.open(archive_path, 'w:gz') as tar: ++ tar.add('.', filter=tarfilter) ++ return archive_path.name ++ ++ ++def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): ++ wheel_directory = Path(wheel_directory).absolute() ++ build_type = 'Release' ++ build_dir = Path(f'build/{build_type}') ++ parallel = os.environ.get('CMAKE_BUILD_PARALLEL_LEVEL', os.cpu_count()) ++ build_cmd = [ ++ sys.executable, ++ 'tools/ci_build/build.py', ++ '--build_dir', 'build', ++ '--skip_submodule_sync', ++ '--skip_tests', ++ '--compile_no_warning_as_error', ++ '--allow_running_as_root', ++ '--cmake_extra_defines', 'onnxruntime_BUILD_UNIT_TESTS=OFF', ++ '--config', build_type, ++ '--enable_pybind', ++ '--parallel', str(parallel), ++ ] ++ if sys.implementation.name == 'graalpy': ++ # The cmake build downloads a bunch of sources that need to be patched ++ subprocess.check_call([*build_cmd, '--update']) ++ marker = build_dir / 'graalpy-patched-marker' ++ if not marker.exists(): ++ subprocess.check_call([sys.executable, '-m', 'autopatch_capi', '.']) ++ with open(marker, 'w') as f: ++ pass ++ subprocess.check_call([*build_cmd, '--build_wheel']) ++ wheels = list((build_dir / 'dist').glob('*.whl')) ++ assert len(wheels) == 1, f"Expected 1 wheel, found {len(wheels)}" ++ wheel = wheels[0] ++ shutil.copyfile(wheel, wheel_directory / wheel.name) ++ return str(wheel.name) +diff --git a/pyproject.toml b/pyproject.toml +index 532e4bf..73152fc 100644 +--- a/pyproject.toml ++++ b/pyproject.toml +@@ -1,6 +1,7 @@ + [build-system] +-requires = ["setuptools>=61.0.0", "wheel", "packaging"] +-build-backend = "setuptools.build_meta" ++requires = ["setuptools>=61.0.0", "wheel", "packaging", "numpy>=1.24.2"] ++build-backend = "onnxruntime_build_backend" ++backend-path = ["."] + + [tool.pydocstyle] + convention = "google" diff --git a/graalpython/lib-graalpython/patches/orjson-3.12.0.patch b/graalpython/lib-graalpython/patches/orjson-3.12.0.patch new file mode 100644 index 0000000000..18b9261a7d --- /dev/null +++ b/graalpython/lib-graalpython/patches/orjson-3.12.0.patch @@ -0,0 +1,396 @@ +diff --git a/build.rs b/build.rs +index 71fe9ba..db1a603 100644 +--- a/build.rs ++++ b/build.rs +@@ -24,6 +24,7 @@ fn main() { + println!("cargo:rustc-cfg=feature=\"inline_str\""); + } + } ++ pyo3_build_config::PythonImplementation::GraalPy => {} + _ => not_supported(&python_config.implementation.to_string()), + } + +diff --git a/src/deserialize/input.rs b/src/deserialize/input.rs +index 8e281e2..e0141c8 100644 +--- a/src/deserialize/input.rs ++++ b/src/deserialize/input.rs +@@ -3,8 +3,10 @@ + + use crate::deserialize::DeserializeError; + #[cfg(all(CPython, not(Py_GIL_DISABLED)))] +-use crate::ffi::{PyByteArrayRef, PyMemoryViewRef}; +-use crate::ffi::{PyBytesRef, PyStrRef}; ++use crate::ffi::PyMemoryViewRef; ++#[cfg(not(CPython))] ++use crate::ffi::{Py_buffer, PyBUF_C_CONTIGUOUS}; ++use crate::ffi::{PyByteArrayRef, PyBytesRef, PyStrRef}; + use std::borrow::Cow; + + const INVALID_STR: &str = "str is not valid UTF-8: surrogates not allowed"; +@@ -16,11 +18,23 @@ const INPUT_TYPE_MESSAGE: &str = "Input must be bytes, bytearray, memoryview, or + const INPUT_TYPE_MESSAGE: &str = "Input must be bytes or str"; + + #[cfg(not(CPython))] +-const INPUT_TYPE_MESSAGE: &str = "Input must be bytes, bytearray, or str"; ++const INPUT_TYPE_MESSAGE: &str = "Input must be bytes, bytearray, memoryview, or str"; + +-#[cfg_attr(not(Py_GIL_DISABLED), repr(transparent))] + pub struct Utf8Buffer { + buffer: &'static str, ++ #[cfg(not(CPython))] ++ view: Option, ++} ++ ++#[cfg(not(CPython))] ++impl Drop for Utf8Buffer { ++ fn drop(&mut self) { ++ if let Some(mut view) = self.view { ++ unsafe { ++ crate::ffi::PyBuffer_Release(&raw mut view); ++ } ++ } ++ } + } + + impl Utf8Buffer { +@@ -41,7 +55,7 @@ impl Utf8Buffer { + } + } + +- #[cfg(any(not(CPython), Py_GIL_DISABLED))] ++ #[cfg(all(CPython, Py_GIL_DISABLED))] + fn buffer_from_ptr( + ptr: *mut crate::ffi::PyObject, + ) -> Result, DeserializeError<'static>> { +@@ -54,6 +68,64 @@ impl Utf8Buffer { + } + } + ++ #[cfg(not(CPython))] ++ pub fn from_pyobject( ++ ptr: *mut crate::ffi::PyObject, ++ ) -> Result> { ++ debug_assert!(!ptr.is_null()); ++ let as_str = if let Ok(ob) = PyBytesRef::from_ptr(ptr) { ++ ob.as_str() ++ } else if let Ok(ob) = PyStrRef::from_ptr(ptr) { ++ ob.as_str() ++ } else if let Ok(ob) = PyByteArrayRef::from_ptr(ptr) { ++ ob.as_str() ++ } else { ++ let mut view = Py_buffer::new(); ++ unsafe { ++ if crate::ffi::PyObject_GetBuffer(ptr, &raw mut view, PyBUF_C_CONTIGUOUS) != 0 { ++ crate::ffi::PyErr_Clear(); ++ return Err(DeserializeError::invalid(Cow::Borrowed(INPUT_TYPE_MESSAGE))); ++ } ++ let buffer = core::slice::from_raw_parts( ++ view.buf.cast::().cast_const(), ++ crate::util::isize_to_usize(view.len), ++ ); ++ if core::str::from_utf8(buffer).is_err() { ++ crate::ffi::PyBuffer_Release(&raw mut view); ++ return Err(DeserializeError::invalid(Cow::Borrowed(INVALID_STR))); ++ } ++ let as_str = core::str::from_utf8_unchecked(buffer); ++ if as_str.is_empty() { ++ crate::ffi::PyBuffer_Release(&raw mut view); ++ return Err(DeserializeError::invalid(Cow::Borrowed( ++ "Input is a zero-length, empty document", ++ ))); ++ } ++ return Ok(Self { ++ buffer: as_str, ++ view: Some(view), ++ }); ++ } ++ }; ++ match as_str { ++ Some(buffer) => { ++ if buffer.is_empty() { ++ cold_path!(); ++ Err(DeserializeError::invalid(Cow::Borrowed( ++ "Input is a zero-length, empty document", ++ ))) ++ } else { ++ Ok(Self { buffer, view: None }) ++ } ++ } ++ None => { ++ cold_path!(); ++ Err(DeserializeError::invalid(Cow::Borrowed(INVALID_STR))) ++ } ++ } ++ } ++ ++ #[cfg(CPython)] + pub fn from_pyobject( + ptr: *mut crate::ffi::PyObject, + ) -> Result> { +diff --git a/src/ffi/compat.rs b/src/ffi/compat.rs +index 68e43a1..2b72a92 100644 +--- a/src/ffi/compat.rs ++++ b/src/ffi/compat.rs +@@ -19,7 +19,7 @@ impl PyTypeRef { + pub(crate) fn from_pyobject(ptr: *mut pyo3_ffi::PyObject) -> Self { + unsafe { + debug_assert!(!ptr.is_null()); +- let type_ptr = (*ptr).ob_type; ++ let type_ptr = PyObject_Type(ptr); + debug_assert!(!type_ptr.is_null()); + Self { + ptr: core::ptr::NonNull::new_unchecked(type_ptr), +@@ -173,14 +173,6 @@ pub(crate) unsafe fn Py_SET_SIZE(op: *mut pyo3_ffi::PyVarObject, size: pyo3_ffi: + unsafe { (*op).ob_size = size } + } + +-#[allow(unused)] +-#[cfg(GraalPy)] +-#[inline(always)] +-#[allow(non_snake_case)] +-pub(crate) unsafe fn Py_SET_SIZE(op: *mut pyo3_ffi::PyVarObject, size: pyo3_ffi::Py_ssize_t) { +- unsafe { (*op)._ob_size_graalpy = size } +-} +- + #[cfg(CPython)] + #[inline(always)] + #[allow(non_snake_case)] +diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs +index 2d0b3be..e5ceb3a 100644 +--- a/src/ffi/mod.rs ++++ b/src/ffi/mod.rs +@@ -10,7 +10,6 @@ mod fragment; + mod iterator; + mod numpy; + mod pyboolref; +-#[cfg(all(CPython, not(Py_GIL_DISABLED)))] + mod pybytearrayref; + mod pybytesref; + mod pydateref; +@@ -45,6 +44,7 @@ pub(crate) use { + fragment::{Fragment, orjson_fragmenttype_new}, + iterator::{PyDictIterator, PyListIterator, PyTupleIterator}, + pyboolref::PyBoolRef, ++ pybytearrayref::{PyByteArrayRef, PyByteArrayRefError}, + pybytesref::{PyBytesRef, PyBytesRefError}, + pydateref::PyDateRef, + pydatetimeref::PyDateTimeRef, +@@ -63,7 +63,6 @@ pub(crate) use { + #[allow(unused_imports)] + #[cfg(all(CPython, not(Py_GIL_DISABLED)))] + pub(crate) use { +- pybytearrayref::{PyByteArrayRef, PyByteArrayRefError}, + pymemoryview::{PyMemoryViewRef, PyMemoryViewRefError}, + }; + +@@ -103,10 +102,49 @@ pub(crate) use pyo3_ffi::PyErr_Restore; + #[cfg(CPython)] + pub(crate) use pyo3_ffi::{PyObject_CallMethodNoArgs, PyObject_CallMethodOneArg}; + ++#[cfg(not(CPython))] ++pub(crate) use pyo3_ffi::{ ++ PyBUF_C_CONTIGUOUS, PyBuffer_Release, PyDateTime_TIME_GET_TZINFO, PyObject_GetBuffer, ++}; ++ ++#[cfg(not(CPython))] ++#[inline(always)] ++#[allow(non_snake_case)] ++pub(crate) unsafe fn PyObject_CallMethodNoArgs( ++ obj: *mut pyo3_ffi::PyObject, ++ name: *mut pyo3_ffi::PyObject, ++) -> *mut pyo3_ffi::PyObject { ++ unsafe { ++ pyo3_ffi::PyObject_CallMethodObjArgs( ++ obj, ++ name, ++ core::ptr::null_mut::(), ++ ) ++ } ++} ++ ++#[cfg(not(CPython))] ++#[inline(always)] ++#[allow(non_snake_case)] ++pub(crate) unsafe fn PyObject_CallMethodOneArg( ++ obj: *mut pyo3_ffi::PyObject, ++ name: *mut pyo3_ffi::PyObject, ++ arg: *mut pyo3_ffi::PyObject, ++) -> *mut pyo3_ffi::PyObject { ++ unsafe { ++ pyo3_ffi::PyObject_CallMethodObjArgs( ++ obj, ++ name, ++ arg, ++ core::ptr::null_mut::(), ++ ) ++ } ++} ++ + #[cfg(all(CPython, not(Py_GIL_DISABLED)))] + pub(crate) use buffer::PyMemoryView_GET_BUFFER; + +-#[cfg(not(feature = "inline_str"))] ++#[cfg(all(CPython, not(feature = "inline_str")))] + pub(crate) use pyo3_ffi::{PyUnicode_DATA, PyUnicode_KIND}; + + #[cfg(Py_3_12)] +diff --git a/src/ffi/pydatetimeref.rs b/src/ffi/pydatetimeref.rs +index 26c0e07..dd0c395 100644 +--- a/src/ffi/pydatetimeref.rs ++++ b/src/ffi/pydatetimeref.rs +@@ -2,8 +2,10 @@ + // Copyright ijl (2025-2026), Ben Sully (2021) + + use crate::typeref::{ +- CONVERT_METHOD_STR, DST_STR, NORMALIZE_METHOD_STR, UTCOFFSET_METHOD_STR, ZONEINFO_TYPE, ++ CONVERT_METHOD_STR, DST_STR, NORMALIZE_METHOD_STR, UTCOFFSET_METHOD_STR, + }; ++#[cfg(CPython)] ++use crate::typeref::ZONEINFO_TYPE; + + use crate::ffi::{PyObject_CallMethodNoArgs, PyObject_CallMethodOneArg, PyObject_HasAttr}; + +@@ -157,7 +159,11 @@ impl PyDateTimeRef { + #[cfg(not(CPython))] + #[inline] + pub fn offset(&self) -> Option { +- unimplemented!() ++ if !self.has_tz() { ++ Some(Offset::default()) ++ } else { ++ self.slow_offset(self.tzinfo()) ++ } + } + + #[cfg(CPython)] +@@ -185,7 +191,6 @@ impl PyDateTimeRef { + } + } + +- #[cfg(CPython)] + #[cold] + #[inline(never)] + fn slow_offset(&self, tzinfo: *mut crate::ffi::PyObject) -> Option { +diff --git a/src/ffi/pydictref.rs b/src/ffi/pydictref.rs +index 4a6c6cf..238b192 100644 +--- a/src/ffi/pydictref.rs ++++ b/src/ffi/pydictref.rs +@@ -19,7 +19,7 @@ impl PartialEq for PyDictRef { + } + } + +-#[cfg(all(Py_3_12, not(Py_GIL_DISABLED)))] ++#[cfg(all(CPython, Py_3_12, not(Py_GIL_DISABLED)))] + #[inline] + fn reverse_pydict_incref(key: *mut pyo3_ffi::PyObject, value: *mut pyo3_ffi::PyObject) { + unsafe { +@@ -32,13 +32,13 @@ fn reverse_pydict_incref(key: *mut pyo3_ffi::PyObject, value: *mut pyo3_ffi::PyO + } + } + +-#[cfg(Py_GIL_DISABLED)] ++#[cfg(any(Py_GIL_DISABLED, not(CPython)))] + #[inline] + fn reverse_pydict_incref(_: *mut pyo3_ffi::PyObject, value: *mut pyo3_ffi::PyObject) { + unsafe { crate::ffi::Py_DECREF(value) } + } + +-#[cfg(not(Py_3_12))] ++#[cfg(all(CPython, not(Py_3_12)))] + #[inline] + fn reverse_pydict_incref(key: *mut pyo3_ffi::PyObject, value: *mut pyo3_ffi::PyObject) { + unsafe { +diff --git a/src/ffi/pystrref/object.rs b/src/ffi/pystrref/object.rs +index 69069fa..bc44e7c 100644 +--- a/src/ffi/pystrref/object.rs ++++ b/src/ffi/pystrref/object.rs +@@ -3,9 +3,10 @@ + + #[allow(unused)] + use crate::ffi::{ +- Py_HashBuffer, Py_SIZE, Py_ssize_t, PyASCIIObject, PyCompactUnicodeObject, PyObject, +- PyUnicode_AsUTF8AndSize, ++ Py_SIZE, Py_ssize_t, PyASCIIObject, PyCompactUnicodeObject, PyObject, PyUnicode_AsUTF8AndSize, + }; ++#[cfg(CPython)] ++use crate::ffi::Py_HashBuffer; + #[cfg(all(CPython, not(feature = "inline_str")))] + use crate::ffi::{PyUnicode_DATA, PyUnicode_KIND}; + use crate::typeref::{EMPTY_UNICODE, STR_TYPE}; +@@ -124,6 +125,12 @@ impl PyStrRef { + obj + } + ++ #[cfg(not(CPython))] ++ #[inline(always)] ++ pub fn from_str_with_hash(buf: &str) -> Self { ++ PyStrRef::from_str(buf) ++ } ++ + #[cfg(CPython)] + #[inline(always)] + pub fn from_str(buf: &str) -> Self { +@@ -166,7 +173,7 @@ impl PyStrRef { + unsafe { crate::ffi::PyUnstable_Unicode_GET_CACHED_HASH(self.as_ptr()) } + } + +- #[cfg(feature = "inline_str")] ++ #[cfg(all(CPython, feature = "inline_str"))] + fn set_hash(&mut self) { + unsafe { + let ptr = self.as_ptr().cast::(); +@@ -187,7 +194,7 @@ impl PyStrRef { + } + } + +- #[cfg(not(feature = "inline_str"))] ++ #[cfg(all(CPython, not(feature = "inline_str")))] + fn set_hash(&mut self) { + unsafe { + let data_ptr = PyUnicode_DATA(self.as_ptr()); +diff --git a/src/ffi/pytimeref.rs b/src/ffi/pytimeref.rs +index 57faa72..04c1ece 100644 +--- a/src/ffi/pytimeref.rs ++++ b/src/ffi/pytimeref.rs +@@ -3,8 +3,10 @@ + + use crate::ffi::{ + PyDateTime_TIME_GET_HOUR, PyDateTime_TIME_GET_MICROSECOND, PyDateTime_TIME_GET_MINUTE, +- PyDateTime_TIME_GET_SECOND, PyDateTime_Time, PyObject, ++ PyDateTime_TIME_GET_SECOND, PyDateTime_TIME_GET_TZINFO, PyObject, + }; ++#[cfg(CPython)] ++use crate::ffi::PyDateTime_Time; + + #[derive(Clone)] + #[repr(transparent)] +@@ -54,7 +56,7 @@ impl PyTimeRef { + #[cfg(not(CPython))] + #[inline] + pub fn has_tz(&self) -> bool { +- unimplemented!() ++ unsafe { PyDateTime_TIME_GET_TZINFO(self.ptr.as_ptr()) != crate::typeref::NONE } + } + + #[inline] +diff --git a/src/util.rs b/src/util.rs +index 1448274..a92e4da 100644 +--- a/src/util.rs ++++ b/src/util.rs +@@ -79,14 +79,14 @@ macro_rules! pydict_contains { + }; + } + +-#[cfg(Py_3_12)] ++#[cfg(all(CPython, Py_3_12))] + macro_rules! use_immortal { + ($op:expr) => { + unsafe { $op } + }; + } + +-#[cfg(not(Py_3_12))] ++#[cfg(any(not(Py_3_12), not(CPython)))] + macro_rules! use_immortal { + ($op:expr) => { + unsafe { diff --git a/graalpython/lib-graalpython/patches/orjson-3.13.0.patch b/graalpython/lib-graalpython/patches/orjson-3.13.0.patch new file mode 100644 index 0000000000..34a05e7569 --- /dev/null +++ b/graalpython/lib-graalpython/patches/orjson-3.13.0.patch @@ -0,0 +1,398 @@ +diff --git a/build.rs b/build.rs +index 6568def..6de0ea8 100644 +--- a/build.rs ++++ b/build.rs +@@ -28,6 +28,7 @@ fn main() { + println!("cargo:rustc-cfg=feature=\"inline_str\""); + } + } ++ pyo3_build_config::PythonImplementation::GraalPy => {} + _ => not_supported(&python_config.implementation().to_string()), + } + +diff --git a/src/deserialize/input.rs b/src/deserialize/input.rs +index 8e281e2..e0141c8 100644 +--- a/src/deserialize/input.rs ++++ b/src/deserialize/input.rs +@@ -3,8 +3,10 @@ + + use crate::deserialize::DeserializeError; + #[cfg(all(CPython, not(Py_GIL_DISABLED)))] +-use crate::ffi::{PyByteArrayRef, PyMemoryViewRef}; +-use crate::ffi::{PyBytesRef, PyStrRef}; ++use crate::ffi::PyMemoryViewRef; ++#[cfg(not(CPython))] ++use crate::ffi::{Py_buffer, PyBUF_C_CONTIGUOUS}; ++use crate::ffi::{PyByteArrayRef, PyBytesRef, PyStrRef}; + use std::borrow::Cow; + + const INVALID_STR: &str = "str is not valid UTF-8: surrogates not allowed"; +@@ -16,11 +18,23 @@ const INPUT_TYPE_MESSAGE: &str = "Input must be bytes, bytearray, memoryview, or + const INPUT_TYPE_MESSAGE: &str = "Input must be bytes or str"; + + #[cfg(not(CPython))] +-const INPUT_TYPE_MESSAGE: &str = "Input must be bytes, bytearray, or str"; ++const INPUT_TYPE_MESSAGE: &str = "Input must be bytes, bytearray, memoryview, or str"; + +-#[cfg_attr(not(Py_GIL_DISABLED), repr(transparent))] + pub struct Utf8Buffer { + buffer: &'static str, ++ #[cfg(not(CPython))] ++ view: Option, ++} ++ ++#[cfg(not(CPython))] ++impl Drop for Utf8Buffer { ++ fn drop(&mut self) { ++ if let Some(mut view) = self.view { ++ unsafe { ++ crate::ffi::PyBuffer_Release(&raw mut view); ++ } ++ } ++ } + } + + impl Utf8Buffer { +@@ -41,7 +55,7 @@ impl Utf8Buffer { + } + } + +- #[cfg(any(not(CPython), Py_GIL_DISABLED))] ++ #[cfg(all(CPython, Py_GIL_DISABLED))] + fn buffer_from_ptr( + ptr: *mut crate::ffi::PyObject, + ) -> Result, DeserializeError<'static>> { +@@ -54,6 +68,64 @@ impl Utf8Buffer { + } + } + ++ #[cfg(not(CPython))] ++ pub fn from_pyobject( ++ ptr: *mut crate::ffi::PyObject, ++ ) -> Result> { ++ debug_assert!(!ptr.is_null()); ++ let as_str = if let Ok(ob) = PyBytesRef::from_ptr(ptr) { ++ ob.as_str() ++ } else if let Ok(ob) = PyStrRef::from_ptr(ptr) { ++ ob.as_str() ++ } else if let Ok(ob) = PyByteArrayRef::from_ptr(ptr) { ++ ob.as_str() ++ } else { ++ let mut view = Py_buffer::new(); ++ unsafe { ++ if crate::ffi::PyObject_GetBuffer(ptr, &raw mut view, PyBUF_C_CONTIGUOUS) != 0 { ++ crate::ffi::PyErr_Clear(); ++ return Err(DeserializeError::invalid(Cow::Borrowed(INPUT_TYPE_MESSAGE))); ++ } ++ let buffer = core::slice::from_raw_parts( ++ view.buf.cast::().cast_const(), ++ crate::util::isize_to_usize(view.len), ++ ); ++ if core::str::from_utf8(buffer).is_err() { ++ crate::ffi::PyBuffer_Release(&raw mut view); ++ return Err(DeserializeError::invalid(Cow::Borrowed(INVALID_STR))); ++ } ++ let as_str = core::str::from_utf8_unchecked(buffer); ++ if as_str.is_empty() { ++ crate::ffi::PyBuffer_Release(&raw mut view); ++ return Err(DeserializeError::invalid(Cow::Borrowed( ++ "Input is a zero-length, empty document", ++ ))); ++ } ++ return Ok(Self { ++ buffer: as_str, ++ view: Some(view), ++ }); ++ } ++ }; ++ match as_str { ++ Some(buffer) => { ++ if buffer.is_empty() { ++ cold_path!(); ++ Err(DeserializeError::invalid(Cow::Borrowed( ++ "Input is a zero-length, empty document", ++ ))) ++ } else { ++ Ok(Self { buffer, view: None }) ++ } ++ } ++ None => { ++ cold_path!(); ++ Err(DeserializeError::invalid(Cow::Borrowed(INVALID_STR))) ++ } ++ } ++ } ++ ++ #[cfg(CPython)] + pub fn from_pyobject( + ptr: *mut crate::ffi::PyObject, + ) -> Result> { +diff --git a/src/ffi/compat.rs b/src/ffi/compat.rs +index 68e43a1..2b72a92 100644 +--- a/src/ffi/compat.rs ++++ b/src/ffi/compat.rs +@@ -19,7 +19,7 @@ impl PyTypeRef { + pub(crate) fn from_pyobject(ptr: *mut pyo3_ffi::PyObject) -> Self { + unsafe { + debug_assert!(!ptr.is_null()); +- let type_ptr = (*ptr).ob_type; ++ let type_ptr = PyObject_Type(ptr); + debug_assert!(!type_ptr.is_null()); + Self { + ptr: core::ptr::NonNull::new_unchecked(type_ptr), +@@ -173,14 +173,6 @@ pub(crate) unsafe fn Py_SET_SIZE(op: *mut pyo3_ffi::PyVarObject, size: pyo3_ffi: + unsafe { (*op).ob_size = size } + } + +-#[allow(unused)] +-#[cfg(GraalPy)] +-#[inline(always)] +-#[allow(non_snake_case)] +-pub(crate) unsafe fn Py_SET_SIZE(op: *mut pyo3_ffi::PyVarObject, size: pyo3_ffi::Py_ssize_t) { +- unsafe { (*op)._ob_size_graalpy = size } +-} +- + #[cfg(CPython)] + #[inline(always)] + #[allow(non_snake_case)] +diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs +index 6cee0c7..da75aa1 100644 +--- a/src/ffi/mod.rs ++++ b/src/ffi/mod.rs +@@ -10,7 +10,6 @@ mod fragment; + mod iterator; + mod numpy; + mod pyboolref; +-#[cfg(all(CPython, not(Py_GIL_DISABLED)))] + mod pybytearrayref; + mod pybytesref; + mod pydateref; +@@ -45,6 +44,7 @@ pub(crate) use { + fragment::{Fragment, orjson_fragmenttype_new}, + iterator::{PyDictIterator, PyListIterator, PyTupleIterator}, + pyboolref::PyBoolRef, ++ pybytearrayref::{PyByteArrayRef, PyByteArrayRefError}, + pybytesref::{PyBytesRef, PyBytesRefError}, + pydateref::PyDateRef, + pydatetimeref::PyDateTimeRef, +@@ -63,7 +63,6 @@ pub(crate) use { + #[allow(unused_imports)] + #[cfg(all(CPython, not(Py_GIL_DISABLED)))] + pub(crate) use { +- pybytearrayref::{PyByteArrayRef, PyByteArrayRefError}, + pymemoryview::{PyMemoryViewRef, PyMemoryViewRefError}, + }; + +@@ -105,12 +104,50 @@ pub(crate) use pyo3_ffi::{ + }; + + #[cfg(not(CPython))] +-pub(crate) use pyo3_ffi::{PyDateTime_DATE_GET_TZINFO, PyDict_SetItem, PyObject_Hash}; ++pub(crate) use pyo3_ffi::{ ++ Py_buffer, PyBUF_C_CONTIGUOUS, PyBuffer_Release, PyByteArray_AsString, PyByteArray_Size, ++ PyByteArray_Type, PyDateTime_DATE_GET_TZINFO, PyDateTime_TIME_GET_TZINFO, PyDict_Contains, ++ PyDict_SetItem, PyObject_GetBuffer, PyObject_Hash, ++}; ++ ++#[cfg(not(CPython))] ++#[inline(always)] ++#[allow(non_snake_case)] ++pub(crate) unsafe fn PyObject_CallMethodNoArgs( ++ obj: *mut pyo3_ffi::PyObject, ++ name: *mut pyo3_ffi::PyObject, ++) -> *mut pyo3_ffi::PyObject { ++ unsafe { ++ pyo3_ffi::PyObject_CallMethodObjArgs( ++ obj, ++ name, ++ core::ptr::null_mut::(), ++ ) ++ } ++} ++ ++#[cfg(not(CPython))] ++#[inline(always)] ++#[allow(non_snake_case)] ++pub(crate) unsafe fn PyObject_CallMethodOneArg( ++ obj: *mut pyo3_ffi::PyObject, ++ name: *mut pyo3_ffi::PyObject, ++ arg: *mut pyo3_ffi::PyObject, ++) -> *mut pyo3_ffi::PyObject { ++ unsafe { ++ pyo3_ffi::PyObject_CallMethodObjArgs( ++ obj, ++ name, ++ arg, ++ core::ptr::null_mut::(), ++ ) ++ } ++} + + #[cfg(all(CPython, not(Py_GIL_DISABLED)))] + pub(crate) use buffer::PyMemoryView_GET_BUFFER; + +-#[cfg(not(feature = "inline_str"))] ++#[cfg(all(CPython, not(feature = "inline_str")))] + pub(crate) use pyo3_ffi::{PyUnicode_DATA, PyUnicode_KIND}; + + #[cfg(Py_3_12)] +diff --git a/src/ffi/pydatetimeref.rs b/src/ffi/pydatetimeref.rs +index 26c0e07..dd0c395 100644 +--- a/src/ffi/pydatetimeref.rs ++++ b/src/ffi/pydatetimeref.rs +@@ -2,8 +2,10 @@ + // Copyright ijl (2025-2026), Ben Sully (2021) + + use crate::typeref::{ +- CONVERT_METHOD_STR, DST_STR, NORMALIZE_METHOD_STR, UTCOFFSET_METHOD_STR, ZONEINFO_TYPE, ++ CONVERT_METHOD_STR, DST_STR, NORMALIZE_METHOD_STR, UTCOFFSET_METHOD_STR, + }; ++#[cfg(CPython)] ++use crate::typeref::ZONEINFO_TYPE; + + use crate::ffi::{PyObject_CallMethodNoArgs, PyObject_CallMethodOneArg, PyObject_HasAttr}; + +@@ -157,7 +159,11 @@ impl PyDateTimeRef { + #[cfg(not(CPython))] + #[inline] + pub fn offset(&self) -> Option { +- unimplemented!() ++ if !self.has_tz() { ++ Some(Offset::default()) ++ } else { ++ self.slow_offset(self.tzinfo()) ++ } + } + + #[cfg(CPython)] +@@ -185,7 +191,6 @@ impl PyDateTimeRef { + } + } + +- #[cfg(CPython)] + #[cold] + #[inline(never)] + fn slow_offset(&self, tzinfo: *mut crate::ffi::PyObject) -> Option { +diff --git a/src/ffi/pydictref.rs b/src/ffi/pydictref.rs +index 5ecc624..64b4f0d 100644 +--- a/src/ffi/pydictref.rs ++++ b/src/ffi/pydictref.rs +@@ -19,7 +19,7 @@ impl PartialEq for PyDictRef { + } + } + +-#[cfg(all(Py_3_12, not(Py_GIL_DISABLED)))] ++#[cfg(all(CPython, Py_3_12, not(Py_GIL_DISABLED)))] + #[inline] + fn reverse_pydict_incref(key: *mut pyo3_ffi::PyObject, value: *mut pyo3_ffi::PyObject) { + unsafe { +@@ -32,13 +32,13 @@ fn reverse_pydict_incref(key: *mut pyo3_ffi::PyObject, value: *mut pyo3_ffi::PyO + } + } + +-#[cfg(Py_GIL_DISABLED)] ++#[cfg(any(Py_GIL_DISABLED, not(CPython)))] + #[inline] + fn reverse_pydict_incref(_: *mut pyo3_ffi::PyObject, value: *mut pyo3_ffi::PyObject) { + unsafe { crate::ffi::Py_DECREF(value) } + } + +-#[cfg(not(Py_3_12))] ++#[cfg(all(CPython, not(Py_3_12)))] + #[inline] + fn reverse_pydict_incref(key: *mut pyo3_ffi::PyObject, value: *mut pyo3_ffi::PyObject) { + unsafe { +diff --git a/src/ffi/pystrref/object.rs b/src/ffi/pystrref/object.rs +index 69069fa..bc44e7c 100644 +--- a/src/ffi/pystrref/object.rs ++++ b/src/ffi/pystrref/object.rs +@@ -3,9 +3,10 @@ + + #[allow(unused)] + use crate::ffi::{ +- Py_HashBuffer, Py_SIZE, Py_ssize_t, PyASCIIObject, PyCompactUnicodeObject, PyObject, +- PyUnicode_AsUTF8AndSize, ++ Py_SIZE, Py_ssize_t, PyASCIIObject, PyCompactUnicodeObject, PyObject, PyUnicode_AsUTF8AndSize, + }; ++#[cfg(CPython)] ++use crate::ffi::Py_HashBuffer; + #[cfg(all(CPython, not(feature = "inline_str")))] + use crate::ffi::{PyUnicode_DATA, PyUnicode_KIND}; + use crate::typeref::{EMPTY_UNICODE, STR_TYPE}; +@@ -124,6 +125,12 @@ impl PyStrRef { + obj + } + ++ #[cfg(not(CPython))] ++ #[inline(always)] ++ pub fn from_str_with_hash(buf: &str) -> Self { ++ PyStrRef::from_str(buf) ++ } ++ + #[cfg(CPython)] + #[inline(always)] + pub fn from_str(buf: &str) -> Self { +@@ -166,7 +173,7 @@ impl PyStrRef { + unsafe { crate::ffi::PyUnstable_Unicode_GET_CACHED_HASH(self.as_ptr()) } + } + +- #[cfg(feature = "inline_str")] ++ #[cfg(all(CPython, feature = "inline_str"))] + fn set_hash(&mut self) { + unsafe { + let ptr = self.as_ptr().cast::(); +@@ -187,7 +194,7 @@ impl PyStrRef { + } + } + +- #[cfg(not(feature = "inline_str"))] ++ #[cfg(all(CPython, not(feature = "inline_str")))] + fn set_hash(&mut self) { + unsafe { + let data_ptr = PyUnicode_DATA(self.as_ptr()); +diff --git a/src/ffi/pytimeref.rs b/src/ffi/pytimeref.rs +index 57faa72..04c1ece 100644 +--- a/src/ffi/pytimeref.rs ++++ b/src/ffi/pytimeref.rs +@@ -3,8 +3,10 @@ + + use crate::ffi::{ + PyDateTime_TIME_GET_HOUR, PyDateTime_TIME_GET_MICROSECOND, PyDateTime_TIME_GET_MINUTE, +- PyDateTime_TIME_GET_SECOND, PyDateTime_Time, PyObject, ++ PyDateTime_TIME_GET_SECOND, PyDateTime_TIME_GET_TZINFO, PyObject, + }; ++#[cfg(CPython)] ++use crate::ffi::PyDateTime_Time; + + #[derive(Clone)] + #[repr(transparent)] +@@ -54,7 +56,7 @@ impl PyTimeRef { + #[cfg(not(CPython))] + #[inline] + pub fn has_tz(&self) -> bool { +- unimplemented!() ++ unsafe { PyDateTime_TIME_GET_TZINFO(self.ptr.as_ptr()) != crate::typeref::NONE } + } + + #[inline] +diff --git a/src/util.rs b/src/util.rs +index 1448274..a92e4da 100644 +--- a/src/util.rs ++++ b/src/util.rs +@@ -79,14 +79,14 @@ macro_rules! pydict_contains { + }; + } + +-#[cfg(Py_3_12)] ++#[cfg(all(CPython, Py_3_12))] + macro_rules! use_immortal { + ($op:expr) => { + unsafe { $op } + }; + } + +-#[cfg(not(Py_3_12))] ++#[cfg(any(not(Py_3_12), not(CPython)))] + macro_rules! use_immortal { + ($op:expr) => { + unsafe { diff --git a/graalpython/lib-graalpython/patches/pyarrow-26.0.0.patch b/graalpython/lib-graalpython/patches/pyarrow-26.0.0.patch new file mode 100644 index 0000000000..95e7ef96ae --- /dev/null +++ b/graalpython/lib-graalpython/patches/pyarrow-26.0.0.patch @@ -0,0 +1,151 @@ +diff --git a/pyarrow/error.pxi b/pyarrow/error.pxi +index cbe2552..8d0d9d9 100644 +--- a/pyarrow/error.pxi ++++ b/pyarrow/error.pxi +@@ -248,7 +248,7 @@ cdef class SignalStopHandler: + if exc_value.signum: + # Re-emit the exact same signal. We restored the Python signal + # handler above, so it should receive it. +- if os.name == 'nt': ++ if os.name == 'nt' or sys.implementation.name == 'graalpy': + SendSignal(exc_value.signum) + else: + SendSignalToThread(exc_value.signum, +diff --git a/pyarrow/memory.pxi b/pyarrow/memory.pxi +index 09df175..23480fc 100644 +--- a/pyarrow/memory.pxi ++++ b/pyarrow/memory.pxi +@@ -20,6 +20,10 @@ + # cython: embedsignature = True + + ++cdef extern from "Python.h": ++ void Py_INCREF(object) ++ ++ + cdef class MemoryPool(_Weakrefable): + """ + Base class for memory allocation. +@@ -34,6 +38,13 @@ cdef class MemoryPool(_Weakrefable): + + cdef void init(self, CMemoryPool* pool): + self.pool = pool ++ # GraalPy change: pyarrow doesn't maintain python references from ++ # buffers to pools, but they dereference the pointer to the pool in the ++ # destructor. They just assume buffers will get GC'ed before their ++ # pools. You can easily get a segfault even on CPython if you make ++ # a buffer outlive its pool. Since we can't guarantee destruction ++ # order, we just leak the pool. ++ Py_INCREF(self) + + def release_unused(self): + """ +diff --git a/pyarrow_build_backend.py b/pyarrow_build_backend.py +new file mode 100644 +index 0000000..2376ea2 +--- /dev/null ++++ b/pyarrow_build_backend.py +@@ -0,0 +1,89 @@ ++import os ++import re ++import tarfile ++import subprocess ++import tempfile ++import tarfile ++import urllib.request ++from pathlib import Path ++from scikit_build_core.build import build_wheel as scikit_build_wheel ++ ++VERSION = re.search(r'set\(PYARROW_VERSION "([^"]+)"\)', Path("CMakeLists.txt").read_text()).group(1) ++ ++ ++def build_sdist(sdist_directory, config_settings=None): ++ nv = f'pyarrow-{VERSION}' ++ srcdir = Path(__file__).parent ++ archive_path = Path(sdist_directory) / f'{nv}.tar.gz' ++ ++ def tarfilter(info): ++ if re.match(r'\./(?:.git|venv|[^-/]+-venv|dist)', info.name): ++ return None ++ info.name = f'./{nv}/{info.name}' ++ return info ++ ++ with tarfile.open(archive_path, 'w:gz') as tar: ++ tar.add('.', filter=tarfilter) ++ return archive_path.name ++ ++ ++def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): ++ wheel_directory = Path(wheel_directory).absolute() ++ with tempfile.TemporaryDirectory() as tmpdir: ++ tmpdir = Path(tmpdir).absolute() ++ tarname = f'apache-arrow-{VERSION}.tar.gz' ++ tarpath = tmpdir / tarname ++ urllib.request.urlretrieve(f"https://github.com/apache/arrow/archive/refs/tags/{tarname}", tarpath) ++ with tarfile.open(tarpath) as tar: ++ tar.extractall(tmpdir) ++ arrow_dir = tmpdir / f'arrow-apache-arrow-{VERSION}' ++ assert arrow_dir.is_dir() ++ arrow_dist = tmpdir / 'arrow-dist' ++ build_dir = tmpdir / 'arrow-build' ++ subprocess.check_call([ ++ 'cmake', '-S', str(arrow_dir / 'cpp'), '-B', str(build_dir), ++ '-DCMAKE_INSTALL_LIBDIR=lib', ++ f'-DCMAKE_INSTALL_PREFIX={arrow_dist}', ++ '-DCMAKE_BUILD_TYPE=Release', ++ '-DARROW_RPATH_ORIGIN=ON', ++ '-DARROW_BUILD_TESTS=OFF', ++ '-DARROW_BUILD_SHARED=ON', ++ '-DARROW_BUILD_STATIC=OFF', ++ # Features ++ '-DARROW_COMPUTE=ON', ++ '-DARROW_CSV=ON', ++ '-DARROW_JSON=ON', ++ '-DARROW_FILESYSTEM=ON', ++ '-DARROW_DATASET=ON', ++ '-DARROW_PARQUET=ON', ++ '-DPARQUET_REQUIRE_ENCRYPTION=ON', ++ '-DARROW_GANDIVA=ON', ++ '-DARROW_WITH_BZ2=ON', ++ '-DARROW_WITH_ZLIB=ON', ++ '-DARROW_WITH_ZSTD=ON', ++ '-DARROW_WITH_LZ4=ON', ++ '-DARROW_WITH_SNAPPY=ON', ++ '-DARROW_WITH_BROTLI=ON', ++ ]) ++ subprocess.check_call([ ++ 'cmake', '--build', str(build_dir), ++ ]) ++ subprocess.check_call([ ++ 'cmake', '--install', str(build_dir), ++ ]) ++ old_env = os.environ.copy() ++ os.environ.update({ ++ 'ARROW_HOME': str(arrow_dist), ++ 'CMAKE_PREFIX_PATH': str(arrow_dist), ++ 'PYARROW_WITH_DATASET': '1', ++ 'PYARROW_WITH_PARQUET': '1', ++ 'PYARROW_WITH_PARQUET_ENCRYPTION': '1', ++ 'PYARROW_WITH_GANDIVA': '1', ++ 'PYARROW_BUNDLE_ARROW_CPP': '1', ++ 'PYARROW_BUNDLE_CYTHON_CPP': '1', ++ }) ++ try: ++ return scikit_build_wheel(wheel_directory, config_settings, metadata_directory) ++ finally: ++ os.environ.clear() ++ os.environ.update(old_env) +diff --git a/pyproject.toml b/pyproject.toml +index 5aa1e85..7acb725 100644 +--- a/pyproject.toml ++++ b/pyproject.toml +@@ -25,7 +25,8 @@ requires = [ + "numpy>=2.0", + "setuptools_scm[toml]>=8", + ] +-build-backend = "scikit_build_core.build" ++build-backend = "pyarrow_build_backend" ++backend-path = ["."] + + [project] + name = "pyarrow" diff --git a/graalpython/lib-graalpython/patches/pyperf-2.11.0.patch b/graalpython/lib-graalpython/patches/pyperf-2.11.0.patch new file mode 100644 index 0000000000..ca45b80c65 --- /dev/null +++ b/graalpython/lib-graalpython/patches/pyperf-2.11.0.patch @@ -0,0 +1,13 @@ +diff --git a/pyperf/_runner.py b/pyperf/_runner.py +index 4271240..43c3c51 100644 +--- a/pyperf/_runner.py ++++ b/pyperf/_runner.py +@@ -77,7 +77,7 @@ class Runner: + loops=0, min_time=0.1, metadata=None, + show_name=True, + program_args=None, add_cmdline_args=None, +- _argparser=None, warmups=1, hooks=None): ++ _argparser=None, warmups=None, hooks=None): + + # Watchdog: ensure that only once instance of Runner (or a Runner + # subclass) is created per process to prevent bad surprises diff --git a/graalpython/lib-graalpython/patches/pyperf-2.3.1.patch b/graalpython/lib-graalpython/patches/pyperf-2.3.1.patch index bfc6eb3417..eb1ee84a10 100644 --- a/graalpython/lib-graalpython/patches/pyperf-2.3.1.patch +++ b/graalpython/lib-graalpython/patches/pyperf-2.3.1.patch @@ -2,12 +2,8 @@ diff --git a/pyperf/_utils.py b/pyperf/_utils.py index 6ada604..f2ba741 100644 --- a/pyperf/_utils.py +++ b/pyperf/_utils.py -@@ -205,7 +205,7 @@ def python_has_jit(): - implementation_name = python_implementation() - if implementation_name == 'pypy': +@@ -207,3 +207,3 @@ def python_has_jit(): return sys.pypy_translation_info["translation.jit"] - elif implementation_name == 'graalpython': + elif implementation_name in ['graalpython', 'graalpy']: return True - elif hasattr(sys, "pyston_version_info") or "pyston_lite" in sys.modules: - return True diff --git a/graalpython/lib-graalpython/patches/rapidfuzz-3.14.1.patch b/graalpython/lib-graalpython/patches/rapidfuzz-3.14.1.patch new file mode 100644 index 0000000000..c93ef3002d --- /dev/null +++ b/graalpython/lib-graalpython/patches/rapidfuzz-3.14.1.patch @@ -0,0 +1,35 @@ +diff --git a/pyproject.toml b/pyproject.toml +index a743c42..21e83b5 100644 +--- a/pyproject.toml ++++ b/pyproject.toml +@@ -1,5 +1,6 @@ + [build-system] + requires = [ ++ "Cython>=3.0.10", + "scikit-build-core>=0.11" + ] + build-backend = "scikit_build_core.build" +diff --git a/src/rapidfuzz/CMakeLists.txt b/src/rapidfuzz/CMakeLists.txt +index 574a198..ac6a42a 100644 +--- a/src/rapidfuzz/CMakeLists.txt ++++ b/src/rapidfuzz/CMakeLists.txt +@@ -1,5 +1,6 @@ + function(create_cython_target _name) +- if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/${_name}.cxx) ++ # Regenerate Cython sources for GraalPy instead of using CPython-generated code. ++ if(FALSE) + set(${_name} + ${CMAKE_CURRENT_LIST_DIR}/${_name}.cxx + PARENT_SCOPE) +diff --git a/src/rapidfuzz/distance/CMakeLists.txt b/src/rapidfuzz/distance/CMakeLists.txt +index b136833..7300081 100644 +--- a/src/rapidfuzz/distance/CMakeLists.txt ++++ b/src/rapidfuzz/distance/CMakeLists.txt +@@ -1,5 +1,6 @@ + function(create_cython_target _name) +- if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/${_name}.cxx) ++ # Regenerate Cython sources for GraalPy instead of using CPython-generated code. ++ if(FALSE) + set(${_name} + ${CMAKE_CURRENT_LIST_DIR}/${_name}.cxx + PARENT_SCOPE) diff --git a/graalpython/lib-graalpython/patches/uvloop-0.23.0.patch b/graalpython/lib-graalpython/patches/uvloop-0.23.0.patch new file mode 100644 index 0000000000..467a9f32ec --- /dev/null +++ b/graalpython/lib-graalpython/patches/uvloop-0.23.0.patch @@ -0,0 +1,33 @@ +diff --git a/setup.py b/setup.py +index 7d4e55b..4d11e97 100644 +--- a/setup.py ++++ b/setup.py +@@ -85,7 +85,7 @@ class uvloop_build_ext(build_ext): + def initialize_options(self): + super().initialize_options() + self.use_system_libuv = False +- self.cython_always = False ++ self.cython_always = True + self.cython_annotate = None + self.cython_directives = None + +diff --git a/uvloop/includes/compat.h b/uvloop/includes/compat.h +index 0c408c9..6fc6c45 100644 +--- a/uvloop/includes/compat.h ++++ b/uvloop/includes/compat.h +@@ -6,6 +6,15 @@ + #include "Python.h" + #include "uv.h" + ++/* Enable Cython's initialization of weakref offsets for static types. ++ * GraalPy's pyconfig.h otherwise disables this code. */ ++#undef CYTHON_USE_TYPE_SLOTS ++#define CYTHON_USE_TYPE_SLOTS 1 ++ ++void PyOS_BeforeFork(void); ++void PyOS_AfterFork_Parent(void); ++void PyOS_AfterFork_Child(void); ++ + + #ifndef EWOULDBLOCK + #define EWOULDBLOCK EAGAIN