From 3e89582a41fc2712a0ce86528be98cf3dd768a23 Mon Sep 17 00:00:00 2001 From: Siu Kwan Lam <1929845+sklam@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:31:01 -0600 Subject: [PATCH 01/61] Minimal changes to get compiling Added pythoncapi_compat.h from https://github.com/python/pythoncapi-compat --- numba/_devicearray.cpp | 2 +- numba/_dispatcher.cpp | 8 +- numba/_dynfunc.c | 12 +- numba/_helperlib.c | 4 +- numba/_pymodule.h | 4 + numba/_typeof.cpp | 8 + numba/core/runtime/_nrt_python.c | 2 +- numba/experimental/jitclass/_box.c | 2 +- numba/mviewbuf.c | 2 +- numba/np/ufunc/_internal.c | 4 +- numba/pythoncapi_compat.h | 1114 ++++++++++++++++++++++++++++ setup.py | 2 +- 12 files changed, 1148 insertions(+), 16 deletions(-) create mode 100644 numba/pythoncapi_compat.h Index: numba-0.60.0/numba/_devicearray.cpp =================================================================== --- numba-0.60.0.orig/numba/_devicearray.cpp +++ numba-0.60.0/numba/_devicearray.cpp @@ -96,7 +96,7 @@ PyTypeObject DeviceArrayType = { /* WARNING: Do not remove this, only modify it! It is a version guard to * act as a reminder to update this struct on Python version update! */ #if (PY_MAJOR_VERSION == 3) -#if ! ((PY_MINOR_VERSION == 9) || (PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11) || (PY_MINOR_VERSION == 12)) +#if ! (NB_SUPPORTED_PYTHON_MINOR) #error "Python minor version is not supported." #endif #else Index: numba-0.60.0/numba/_dispatcher.cpp =================================================================== --- numba-0.60.0.orig/numba/_dispatcher.cpp +++ numba-0.60.0/numba/_dispatcher.cpp @@ -27,7 +27,7 @@ * */ -#if (PY_MAJOR_VERSION >= 3) && (PY_MINOR_VERSION == 12) +#if (PY_MAJOR_VERSION >= 3) && (PY_MINOR_VERSION == 12) || (PY_MAJOR_VERSION >= 3) && (PY_MINOR_VERSION == 13) #ifndef Py_BUILD_CORE #define Py_BUILD_CORE 1 @@ -39,7 +39,10 @@ # undef HAVE_STD_ATOMIC #endif #undef _PyGC_FINALIZED -#include "internal/pycore_atomic.h" + +#if (PY_MINOR_VERSION == 12) + #include "internal/pycore_atomic.h" +#endif #include "internal/pycore_interp.h" #include "internal/pycore_pyerrors.h" #include "internal/pycore_instruments.h" @@ -780,7 +783,7 @@ call_cfunc(Dispatcher *self, PyObject *c } } -#elif (PY_MAJOR_VERSION >= 3) && (PY_MINOR_VERSION == 12) +#elif (PY_MAJOR_VERSION >= 3) && ((PY_MINOR_VERSION == 12) || (PY_MINOR_VERSION == 13)) // Python 3.12 has a completely new approach to tracing and profiling due to // the new `sys.monitoring` system. @@ -1589,7 +1592,7 @@ static PyTypeObject DispatcherType = { 0, /* tp_version_tag */ 0, /* tp_finalize */ 0, /* tp_vectorcall */ -#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION == 12) +#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION == 12) || (PY_MAJOR_VERSION >= 3) && (PY_MINOR_VERSION == 13) /* This was introduced first in 3.12 * https://github.com/python/cpython/issues/91051 */ @@ -1599,7 +1602,7 @@ static PyTypeObject DispatcherType = { /* WARNING: Do not remove this, only modify it! It is a version guard to * act as a reminder to update this struct on Python version update! */ #if (PY_MAJOR_VERSION == 3) -#if ! ((PY_MINOR_VERSION == 9) || (PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11) || (PY_MINOR_VERSION == 12)) +#if ! (NB_SUPPORTED_PYTHON_MINOR) #error "Python minor version is not supported." #endif #else Index: numba-0.60.0/numba/_dynfunc.c =================================================================== --- numba-0.60.0.orig/numba/_dynfunc.c +++ numba-0.60.0/numba/_dynfunc.c @@ -7,6 +7,12 @@ #include + +// if python version is 3.13 +#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION == 13) + #include "pythoncapi_compat.h" + #define _Py_IsFinalizing Py_IsFinalizing +#endif /* NOTE: EnvironmentObject and ClosureObject must be kept in sync with * the definitions in numba/targets/base.py (EnvBody and ClosureBody). */ @@ -146,7 +152,7 @@ static PyTypeObject EnvironmentType = { /* WARNING: Do not remove this, only modify it! It is a version guard to * act as a reminder to update this struct on Python version update! */ #if (PY_MAJOR_VERSION == 3) -#if ! ((PY_MINOR_VERSION == 9) || (PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11) || (PY_MINOR_VERSION == 12)) +#if ! (NB_SUPPORTED_PYTHON_MINOR) #error "Python minor version is not supported." #endif #else @@ -265,7 +271,7 @@ static PyTypeObject ClosureType = { /* WARNING: Do not remove this, only modify it! It is a version guard to * act as a reminder to update this struct on Python version update! */ #if (PY_MAJOR_VERSION == 3) -#if ! ((PY_MINOR_VERSION == 9) || (PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11) || (PY_MINOR_VERSION == 12)) +#if ! (NB_SUPPORTED_PYTHON_MINOR) #error "Python minor version is not supported." #endif #else @@ -485,7 +491,7 @@ static PyTypeObject GeneratorType = { /* WARNING: Do not remove this, only modify it! It is a version guard to * act as a reminder to update this struct on Python version update! */ #if (PY_MAJOR_VERSION == 3) -#if ! ((PY_MINOR_VERSION == 9) || (PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11) || (PY_MINOR_VERSION == 12)) +#if ! (NB_SUPPORTED_PYTHON_MINOR) #error "Python minor version is not supported." #endif #else Index: numba-0.60.0/numba/_helperlib.c =================================================================== --- numba-0.60.0.orig/numba/_helperlib.c +++ numba-0.60.0/numba/_helperlib.c @@ -293,7 +293,7 @@ numba_recreate_record(void *pdata, int s return NULL; } - numpy = PyImport_ImportModuleNoBlock("numpy"); + numpy = PyImport_ImportModule("numpy"); if (!numpy) goto CLEANUP; numpy_record = PyObject_GetAttrString(numpy, "record"); @@ -833,7 +833,7 @@ static void traceback_add(const char *fu if (!frame) goto error; -#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION == 12) /* 3.12 */ +#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION == 12) || (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION == 13) /* 3.12 or 3.13 */ #elif (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION == 11) /* 3.11 */ /* unsafe cast to our copy of _frame to access the f_lineno field */ @@ -851,7 +851,7 @@ static void traceback_add(const char *fu Py_DECREF(frame); return; -#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION == 12) /* 3.12 */ +#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION == 12) || (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION == 13) /* 3.12 or 3.13 */ error: _PyErr_ChainExceptions1(exc); #elif (PY_MAJOR_VERSION == 3) && ((PY_MINOR_VERSION == 9) || (PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11)) /* 3.11 and below */ Index: numba-0.60.0/numba/_pymodule.h =================================================================== --- numba-0.60.0.orig/numba/_pymodule.h +++ numba-0.60.0/numba/_pymodule.h @@ -29,4 +29,7 @@ PyObject_SetAttrString(m, #name, tmp); \ Py_DECREF(tmp); } while (0) + +#define NB_SUPPORTED_PYTHON_MINOR ((PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11) || (PY_MINOR_VERSION == 12) || (PY_MINOR_VERSION == 13)) + #endif /* NUMBA_PY_MODULE_H_ */ Index: numba-0.60.0/numba/_typeof.cpp =================================================================== --- numba-0.60.0.orig/numba/_typeof.cpp +++ numba-0.60.0/numba/_typeof.cpp @@ -16,6 +16,14 @@ #include #endif +#if (PY_MAJOR_VERSION >= 3) && (PY_MINOR_VERSION == 13) + #ifndef Py_BUILD_CORE + #define Py_BUILD_CORE 1 + #endif + #include "internal/pycore_setobject.h" // _PySet_NextEntry() +#endif + + /* Cached typecodes for basic scalar types */ static int tc_int8; static int tc_int16; Index: numba-0.60.0/numba/core/runtime/_nrt_python.c =================================================================== --- numba-0.60.0.orig/numba/core/runtime/_nrt_python.c +++ numba-0.60.0/numba/core/runtime/_nrt_python.c @@ -229,7 +229,7 @@ static PyTypeObject MemInfoType = { /* WARNING: Do not remove this, only modify it! It is a version guard to * act as a reminder to update this struct on Python version update! */ #if (PY_MAJOR_VERSION == 3) -#if ! ((PY_MINOR_VERSION == 9) || (PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11) || (PY_MINOR_VERSION == 12)) +#if ! (NB_SUPPORTED_PYTHON_MINOR) #error "Python minor version is not supported." #endif #else Index: numba-0.60.0/numba/experimental/jitclass/_box.c =================================================================== --- numba-0.60.0.orig/numba/experimental/jitclass/_box.c +++ numba-0.60.0/numba/experimental/jitclass/_box.c @@ -110,7 +110,7 @@ static PyTypeObject BoxType = { /* WARNING: Do not remove this, only modify it! It is a version guard to * act as a reminder to update this struct on Python version update! */ #if (PY_MAJOR_VERSION == 3) -#if ! ((PY_MINOR_VERSION == 9) || (PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11) || (PY_MINOR_VERSION == 12)) +#if ! (NB_SUPPORTED_PYTHON_MINOR) #error "Python minor version is not supported." #endif #else Index: numba-0.60.0/numba/mviewbuf.c =================================================================== --- numba-0.60.0.orig/numba/mviewbuf.c +++ numba-0.60.0/numba/mviewbuf.c @@ -344,7 +344,7 @@ static PyTypeObject MemAllocType = { /* WARNING: Do not remove this, only modify it! It is a version guard to * act as a reminder to update this struct on Python version update! */ #if (PY_MAJOR_VERSION == 3) -#if ! ((PY_MINOR_VERSION == 9) || (PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11) || (PY_MINOR_VERSION == 12)) +#if ! (NB_SUPPORTED_PYTHON_MINOR) #error "Python minor version is not supported." #endif #else Index: numba-0.60.0/numba/np/ufunc/_internal.c =================================================================== --- numba-0.60.0.orig/numba/np/ufunc/_internal.c +++ numba-0.60.0/numba/np/ufunc/_internal.c @@ -100,7 +100,7 @@ PyTypeObject PyUFuncCleaner_Type = { /* WARNING: Do not remove this, only modify it! It is a version guard to * act as a reminder to update this struct on Python version update! */ #if (PY_MAJOR_VERSION == 3) -#if ! ((PY_MINOR_VERSION == 9) || (PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11) || (PY_MINOR_VERSION == 12)) +#if ! (NB_SUPPORTED_PYTHON_MINOR) #error "Python minor version is not supported." #endif #else @@ -753,7 +753,7 @@ PyTypeObject PyDUFunc_Type = { /* WARNING: Do not remove this, only modify it! It is a version guard to * act as a reminder to update this struct on Python version update! */ #if (PY_MAJOR_VERSION == 3) -#if ! ((PY_MINOR_VERSION == 9) || (PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11) || (PY_MINOR_VERSION == 12)) +#if ! (NB_SUPPORTED_PYTHON_MINOR) #error "Python minor version is not supported." #endif #else Index: numba-0.60.0/numba/core/bytecode.py =================================================================== --- numba-0.60.0.orig/numba/core/bytecode.py +++ numba-0.60.0/numba/core/bytecode.py @@ -9,7 +9,7 @@ from numba.core import errors, utils, se from numba.core.utils import PYVERSION -if PYVERSION in ((3, 12), ): +if PYVERSION in ((3, 12), (3, 13)): from opcode import _inline_cache_entries # Instruction/opcode length in bytes INSTR_LEN = 2 @@ -104,7 +104,12 @@ class ByteCodeInst(object): # https://bugs.python.org/issue27129 # https://github.com/python/cpython/pull/25069 assert self.is_jump - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 13),): + if self.opcode in (dis.opmap[k] + for k in ["JUMP_BACKWARD", + "JUMP_BACKWARD_NO_INTERRUPT"]): + return self.next - (self.arg * 2) + elif PYVERSION in ((3, 12),): if self.opcode in (dis.opmap[k] for k in ["JUMP_BACKWARD"]): return self.offset - (self.arg - 1) * 2 @@ -121,7 +126,7 @@ class ByteCodeInst(object): else: raise NotImplementedError(PYVERSION) - if PYVERSION in ((3, 10), (3, 11), (3, 12)): + if PYVERSION in ((3, 10), (3, 11), (3, 12), (3, 13)): if self.opcode in JREL_OPS: return self.next + self.arg * 2 else: @@ -160,7 +165,7 @@ OPCODE_NOP = dis.opname.index('NOP') # Adapted from Lib/dis.py -def _unpack_opargs(code): +def _unpack_opargs_pre_3_13(code): """ Returns a 4-int-tuple of (bytecode offset, opcode, argument, offset of next bytecode). @@ -176,7 +181,7 @@ def _unpack_opargs(code): for j in range(ARG_LEN): arg |= code[i + j] << (8 * j) i += ARG_LEN - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12),): # Python 3.12 introduced cache slots. We need to account for # cache slots when we determine the offset of the next opcode. # The number of cache slots is specific to each opcode and can @@ -200,7 +205,7 @@ def _unpack_opargs(code): else: arg = None i += NO_ARG_LEN - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12),): # Python 3.12 introduced cache slots. We need to account for # cache slots when we determine the offset of the next opcode. # The number of cache slots is specific to each opcode and can @@ -216,6 +221,80 @@ def _unpack_opargs(code): offset = i # Mark inst offset at first extended +# Adapted from Lib/dis.py +def _unpack_opargs_pre_3_13(code): + """ + Returns a 4-int-tuple of + (bytecode offset, opcode, argument, offset of next bytecode). + """ + extended_arg = 0 + n = len(code) + offset = i = 0 + while i < n: + op = code[i] + i += CODE_LEN + if op >= HAVE_ARGUMENT: + arg = code[i] | extended_arg + for j in range(ARG_LEN): + arg |= code[i + j] << (8 * j) + i += ARG_LEN + if PYVERSION in ((3, 12),): + # Python 3.12 introduced cache slots. We need to account for + # cache slots when we determine the offset of the next opcode. + # The number of cache slots is specific to each opcode and can + # be looked up in the _inline_cache_entries dictionary. + i += _inline_cache_entries[op] * INSTR_LEN + elif PYVERSION in ((3, 10), (3, 11)): + pass + else: + raise NotImplementedError(PYVERSION) + if op == EXTENDED_ARG: + # This is a deviation from what dis does... + # In python 3.11 it seems like EXTENDED_ARGs appear more often + # and are also used as jump targets. So as to not have to do + # "book keeping" for where EXTENDED_ARGs have been "skipped" + # they are replaced with NOPs so as to provide a legal jump + # target and also ensure that the bytecode offsets are correct. + yield (offset, OPCODE_NOP, arg, i) + extended_arg = arg << 8 * ARG_LEN + offset = i + continue + else: + arg = None + i += NO_ARG_LEN + if PYVERSION in ((3, 12),): + # Python 3.12 introduced cache slots. We need to account for + # cache slots when we determine the offset of the next opcode. + # The number of cache slots is specific to each opcode and can + # be looked up in the _inline_cache_entries dictionary. + i += _inline_cache_entries[op] * INSTR_LEN + elif PYVERSION in ((3, 10), (3, 11)): + pass + else: + raise NotImplementedError(PYVERSION) + + extended_arg = 0 + yield (offset, op, arg, i) + offset = i # Mark inst offset at first extended + + +if PYVERSION in ((3, 13),): + + def _unpack_opargs(code): + buf = [] + for i, start_offset, op, arg in dis._unpack_opargs(code): + buf.append((start_offset, op, arg)) + for i, (start_offset, op, arg) in enumerate(buf): + if i + 1 < len(buf): + next_offset = buf[i + 1][0] + else: + next_offset = len(code) + yield (start_offset, op, arg, next_offset) + +else: + _unpack_opargs = _unpack_opargs_pre_3_13 + + def _patched_opargs(bc_stream): """Patch the bytecode stream. @@ -298,7 +377,7 @@ class _ByteCode(object): # Start with first bytecode's lineno known = code.co_firstlineno for inst in table.values(): - if inst.lineno >= 0: + if inst.lineno is not None and inst.lineno >= 0: known = inst.lineno else: inst.lineno = known @@ -363,7 +442,7 @@ class _ByteCode(object): def _fix_LOAD_GLOBAL_arg(arg): - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): return arg >> 1 elif PYVERSION in ((3, 9), (3, 10)): return arg @@ -452,8 +531,15 @@ class ByteCodePy312(ByteCodePy311): entirely along with the dead exceptions that it points to. A pair of exception that sandwiches these exception will also be merged into a single exception. - """ + Update for Python 3.13, the ending of the pattern has a extra + POP_TOP: + + ... + END_FOR + POP_TOP + SWAP(2) + """ def pop_and_merge_exceptions(entries: list, entry_to_remove: _ExceptionTableEntry): lower_entry_idx = entries.index(entry_to_remove) - 1 @@ -505,17 +591,34 @@ class ByteCodePy312(ByteCodePy311): if not next_inst.opname == "FOR_ITER": continue - # Check end of pattern, two instructions. - # Check for the corresponding END_FOR, exception table end is - # non-inclusive, so subtract one. - index = self.ordered_offsets.index(entry.end) - curr_inst = self.table[self.ordered_offsets[index - 1]] - if not curr_inst.opname == "END_FOR": - continue - # END_FOR must be followed by SWAP(2) - next_inst = self.table[self.ordered_offsets[index]] - if not next_inst.opname == "SWAP" and next_inst.arg == 2: - continue + if PYVERSION == (3, 13): + # Check end of pattern, two instructions. + # Check for the corresponding END_FOR, exception table end + # is non-inclusive, so subtract one. + index = self.ordered_offsets.index(entry.end) + curr_inst = self.table[self.ordered_offsets[index - 2]] + if not curr_inst.opname == "END_FOR": + continue + next_inst = self.table[self.ordered_offsets[index - 1]] + if not next_inst.opname == "POP_TOP": + continue + # END_FOR must be followed by SWAP(2) + next_inst = self.table[self.ordered_offsets[index]] + if not next_inst.opname == "SWAP" and next_inst.arg == 2: + continue + else: + assert PYVERSION < (3, 13) + # Check end of pattern, two instructions. + # Check for the corresponding END_FOR, exception table end + # is non-inclusive, so subtract one. + index = self.ordered_offsets.index(entry.end) + curr_inst = self.table[self.ordered_offsets[index - 1]] + if not curr_inst.opname == "END_FOR": + continue + # END_FOR must be followed by SWAP(2) + next_inst = self.table[self.ordered_offsets[index]] + if not next_inst.opname == "SWAP" and next_inst.arg == 2: + continue # If all conditions are met that means this exception entry # is for a list/dict/set comprehension and can be removed. # Also if there exist exception entries above and below this @@ -528,7 +631,7 @@ class ByteCodePy312(ByteCodePy311): if PYVERSION == (3, 11): ByteCode = ByteCodePy311 -elif PYVERSION == (3, 12): +elif PYVERSION in ((3, 12), (3, 13),): ByteCode = ByteCodePy312 elif PYVERSION < (3, 11): ByteCode = _ByteCode Index: numba-0.60.0/numba/core/byteflow.py =================================================================== --- numba-0.60.0.orig/numba/core/byteflow.py +++ numba-0.60.0/numba/core/byteflow.py @@ -10,7 +10,7 @@ from functools import total_ordering from numba.core.utils import UniqueDict, PYVERSION, ALL_BINOPS_TO_OPERATORS from numba.core.controlflow import NEW_BLOCKERS, CFGraph from numba.core.ir import Loc -from numba.core.errors import UnsupportedError +from numba.core.errors import UnsupportedBytecodeError _logger = logging.getLogger(__name__) @@ -24,7 +24,7 @@ _NO_RAISE_OPS = frozenset({ 'PRECALL', }) -if PYVERSION in ((3, 12), ): +if PYVERSION in ((3, 12), (3, 13)): from enum import Enum # Operands for CALL_INTRINSIC_1 @@ -149,7 +149,7 @@ class Flow(object): self.block_infos[state.pc_initial] = si = adapt_state_infos(state) _logger.debug("block_infos %s:\n%s", state, si) - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): def _run_handle_exception(self, runner, state): if not state.in_with() and ( state.has_active_try() and @@ -312,7 +312,7 @@ class Flow(object): msg = ("The 'with (context manager) as " "(variable):' construct is not " "supported.") - raise UnsupportedError(msg) + raise UnsupportedBytecodeError(msg) def _is_null_temp_reg(reg): @@ -331,7 +331,7 @@ class TraceRunner(object): return Loc(self.debug_filename, lineno) def dispatch(self, state): - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): if state._blockstack: state: State while state._blockstack: @@ -354,7 +354,8 @@ class TraceRunner(object): fn(state, inst) else: msg = "Use of unsupported opcode (%s) found" % inst.opname - raise UnsupportedError(msg, loc=self.get_debug_loc(inst.lineno)) + raise UnsupportedBytecodeError(msg, + loc=self.get_debug_loc(inst.lineno)) def _adjust_except_stack(self, state): """ @@ -405,6 +406,15 @@ class TraceRunner(object): state.push(state.make_temp()) state.append(inst) + if PYVERSION in ((3, 13),): + def op_FORMAT_SIMPLE(self, state, inst): + assert PYVERSION == (3, 13) + value = state.pop() + strvar = state.make_temp() + res = state.make_temp() + state.append(inst, value=value, res=res, strvar=strvar) + state.push(res) + def op_FORMAT_VALUE(self, state, inst): """ FORMAT_VALUE(flags): flags argument specifies format spec which is @@ -415,7 +425,8 @@ class TraceRunner(object): """ if inst.arg != 0: msg = "format spec in f-strings not supported yet" - raise UnsupportedError(msg, loc=self.get_debug_loc(inst.lineno)) + raise UnsupportedBytecodeError(msg, + loc=self.get_debug_loc(inst.lineno)) value = state.pop() strvar = state.make_temp() res = state.make_temp() @@ -442,7 +453,27 @@ class TraceRunner(object): def op_POP_TOP(self, state, inst): state.pop() - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 13),): + def op_TO_BOOL(self, state, inst): + res = state.make_temp() + tos = state.pop() + state.append(inst, val=tos, res=res) + state.push(res) + + elif PYVERSION < (3, 13): + pass + + if PYVERSION in ((3, 13),): + def op_LOAD_GLOBAL(self, state, inst): + # Ordering of the global value and NULL is swapped in Py3.13 + res = state.make_temp() + idx = inst.arg >> 1 + state.append(inst, idx=idx, res=res) + state.push(res) + # ignoring the NULL + if inst.arg & 1: + state.push(state.make_null()) + elif PYVERSION in ((3, 11), (3, 12)): def op_LOAD_GLOBAL(self, state, inst): res = state.make_temp() idx = inst.arg >> 1 @@ -471,30 +502,89 @@ class TraceRunner(object): state.push(res) def op_LOAD_CONST(self, state, inst): - res = state.make_temp("const") + # append const index for interpreter to read the const value + res = state.make_temp("const") + f".{inst.arg}" state.push(res) state.append(inst, res=res) def op_LOAD_ATTR(self, state, inst): item = state.pop() - if PYVERSION in ((3, 12), ): + res = state.make_temp() + if PYVERSION in ((3, 13),): + state.push(res) # the attr + if inst.arg & 1: + state.push(state.make_null()) + elif PYVERSION in ((3, 12),): if inst.arg & 1: state.push(state.make_null()) + state.push(res) elif PYVERSION in ((3, 9), (3, 10), (3, 11)): - pass + state.push(res) else: raise NotImplementedError(PYVERSION) - res = state.make_temp() state.append(inst, item=item, res=res) - state.push(res) def op_LOAD_FAST(self, state, inst): - name = state.get_varname(inst) + assert PYVERSION <= (3, 13) + if PYVERSION in ((3, 13), ): + try: + name = state.get_varname(inst) + except IndexError: # oparg is out of range + # Handle this like a LOAD_DEREF + # Assume MAKE_CELL and COPY_FREE_VARS has correctly setup the + # states. + # According to https://github.com/python/cpython/blob/9ac606080a0074cdf7589d9b7c9413a73e0ddf37/Objects/codeobject.c#L730C9-L759 # noqa E501 + # localsplus is locals + cells + freevars + bc = state._bytecode + num_varnames = len(bc.co_varnames) + num_freevars = len(bc.co_freevars) + num_cellvars = len(bc.co_cellvars) + max_fast_local = num_cellvars + num_freevars + assert 0 <= inst.arg - num_varnames < max_fast_local + res = state.make_temp() + state.append(inst, res=res, as_load_deref=True) + state.push(res) + return + else: + name = state.get_varname(inst) res = state.make_temp(name) state.append(inst, res=res) state.push(res) - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 13),): + def op_LOAD_FAST_LOAD_FAST(self, state, inst): + oparg = inst.arg + oparg1 = oparg >> 4 + oparg2 = oparg & 15 + name1 = state.get_varname_by_arg(oparg1) + name2 = state.get_varname_by_arg(oparg2) + res1 = state.make_temp(name1) + res2 = state.make_temp(name2) + state.append(inst, res1=res1, res2=res2) + state.push(res1) + state.push(res2) + + def op_STORE_FAST_LOAD_FAST(self, state, inst): + oparg = inst.arg + # oparg1 = oparg >> 4 # not needed + oparg2 = oparg & 15 + store_value = state.pop() + load_name = state.get_varname_by_arg(oparg2) + load_res = state.make_temp(load_name) + state.append(inst, store_value=store_value, load_res=load_res) + state.push(load_res) + + def op_STORE_FAST_STORE_FAST(self, state, inst): + value1 = state.pop() + value2 = state.pop() + state.append(inst, value1=value1, value2=value2) + + elif PYVERSION in ((3, 10), (3, 11), (3, 12)): + pass + else: + raise NotImplementedError(PYVERSION) + + if PYVERSION in ((3, 12), (3, 13)): op_LOAD_FAST_CHECK = op_LOAD_FAST op_LOAD_FAST_AND_CLEAR = op_LOAD_FAST elif PYVERSION in ((3, 9), (3, 10), (3, 11)): @@ -753,7 +843,7 @@ class TraceRunner(object): ) state.push(res) - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): def op_BINARY_SLICE(self, state, inst): end = state.pop() start = state.pop() @@ -771,7 +861,7 @@ class TraceRunner(object): else: raise NotImplementedError(PYVERSION) - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): def op_STORE_SLICE(self, state, inst): end = state.pop() start = state.pop() @@ -804,7 +894,7 @@ class TraceRunner(object): op_POP_JUMP_IF_TRUE = _op_POP_JUMP_IF op_POP_JUMP_IF_FALSE = _op_POP_JUMP_IF - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): op_POP_JUMP_IF_NONE = _op_POP_JUMP_IF op_POP_JUMP_IF_NOT_NONE = _op_POP_JUMP_IF elif PYVERSION in ((3, 9), (3, 10), (3, 11)): @@ -853,6 +943,8 @@ class TraceRunner(object): state.append(inst) state.fork(pc=inst.get_jump_target()) + op_JUMP_BACKWARD_NO_INTERRUPT = op_JUMP_BACKWARD + def op_JUMP_ABSOLUTE(self, state, inst): state.append(inst) state.fork(pc=inst.get_jump_target()) @@ -868,7 +960,7 @@ class TraceRunner(object): state.append(inst, retval=state.pop(), castval=state.make_temp()) state.terminate() - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): def op_RETURN_CONST(self, state, inst): res = state.make_temp("const") state.append(inst, retval=res, castval=state.make_temp()) @@ -884,14 +976,14 @@ class TraceRunner(object): state.append(inst, value=val, res=res) state.push(res) - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): def op_RAISE_VARARGS(self, state, inst): if inst.arg == 0: exc = None # No re-raising within a try-except block. # But we allow bare reraise. if state.has_active_try(): - raise UnsupportedError( + raise UnsupportedBytecodeError( "The re-raising of an exception is not yet supported.", loc=self.get_debug_loc(inst.lineno), ) @@ -915,7 +1007,7 @@ class TraceRunner(object): if inst.arg == 0: exc = None if in_exc_block: - raise UnsupportedError( + raise UnsupportedBytecodeError( "The re-raising of an exception is not yet supported.", loc=self.get_debug_loc(inst.lineno), ) @@ -940,7 +1032,10 @@ class TraceRunner(object): blk = state.pop_block() state.reset_stack(blk['entry_stack']) - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 13),): + def op_END_FOR(self, state, inst): + state.pop() + elif PYVERSION in ((3, 12),): def op_END_FOR(self, state, inst): state.pop() state.pop() @@ -954,7 +1049,8 @@ class TraceRunner(object): if inst.arg != 0: msg = ('Unsupported use of a bytecode related to try..finally' ' or a with-context') - raise UnsupportedError(msg, loc=self.get_debug_loc(inst.lineno)) + raise UnsupportedBytecodeError(msg, + loc=self.get_debug_loc(inst.lineno)) def op_CALL_FINALLY(self, state, inst): pass @@ -1068,7 +1164,7 @@ class TraceRunner(object): 'FINALLY', state, next=inst.next, end=inst.get_jump_target(), ) - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): def op_POP_EXCEPT(self, state, inst): state.pop() @@ -1076,7 +1172,7 @@ class TraceRunner(object): def op_POP_EXCEPT(self, state, inst): blk = state.pop_block() if blk['kind'] not in {BlockKind('EXCEPT'), BlockKind('FINALLY')}: - raise UnsupportedError( + raise UnsupportedBytecodeError( f"POP_EXCEPT got an unexpected block: {blk['kind']}", loc=self.get_debug_loc(inst.lineno), ) @@ -1117,16 +1213,24 @@ class TraceRunner(object): def op_CALL(self, state, inst): narg = inst.arg args = list(reversed([state.pop() for _ in range(narg)])) - callable_or_firstarg = state.pop() - null_or_callable = state.pop() - if _is_null_temp_reg(null_or_callable): - callable = callable_or_firstarg - else: - callable = null_or_callable - args = [callable_or_firstarg, *args] + if PYVERSION == (3, 13): + null_or_self = state.pop() + # position of the callable is fixed + callable = state.pop() + if not _is_null_temp_reg(null_or_self): + args = [null_or_self, *args] + kw_names = None + elif PYVERSION < (3, 13): + callable_or_firstarg = state.pop() + null_or_callable = state.pop() + if _is_null_temp_reg(null_or_callable): + callable = callable_or_firstarg + else: + callable = null_or_callable + args = [callable_or_firstarg, *args] + kw_names = state.pop_kw_names() res = state.make_temp() - kw_names = state.pop_kw_names() state.append(inst, func=callable, args=args, kw_names=kw_names, res=res) state.push(res) @@ -1152,28 +1256,67 @@ class TraceRunner(object): state.append(inst, func=func, args=args, names=names, res=res) state.push(res) - def op_CALL_FUNCTION_EX(self, state, inst): - if inst.arg & 1 and PYVERSION < (3, 10): - errmsg = "CALL_FUNCTION_EX with **kwargs not supported" - raise UnsupportedError(errmsg) - if inst.arg & 1: - varkwarg = state.pop() - else: - varkwarg = None - vararg = state.pop() - func = state.pop() + if PYVERSION in ((3, 13),): + def op_CALL_KW(self, state, inst): + narg = inst.arg + kw_names = state.pop() + args = list(reversed([state.pop() for _ in range(narg)])) + null_or_firstarg = state.pop() + callable = state.pop() + if not _is_null_temp_reg(null_or_firstarg): + args = [null_or_firstarg, *args] - if PYVERSION in ((3, 11), (3, 12)): - if _is_null_temp_reg(state.peek(1)): - state.pop() # pop NULL, it's not used - elif PYVERSION in ((3, 9), (3, 10)): - pass - else: - raise NotImplementedError(PYVERSION) + res = state.make_temp() + state.append(inst, func=callable, args=args, kw_names=kw_names, + res=res) + state.push(res) - res = state.make_temp() - state.append(inst, func=func, vararg=vararg, varkwarg=varkwarg, res=res) - state.push(res) + elif PYVERSION in ((3, 10), (3, 11), (3, 12)): + pass + else: + raise NotImplementedError(PYVERSION) + + if PYVERSION in ((3, 13),): + def op_CALL_FUNCTION_EX(self, state, inst): + # (func, unused, callargs, kwargs if (oparg & 1) -- result)) + if inst.arg & 1: + varkwarg = state.pop() + else: + varkwarg = None + + vararg = state.pop() + state.pop() # unused + func = state.pop() + + res = state.make_temp() + state.append(inst, func=func, vararg=vararg, varkwarg=varkwarg, + res=res) + state.push(res) + + elif PYVERSION in ((3, 10), (3, 11), (3, 12)): + + def op_CALL_FUNCTION_EX(self, state, inst): + if inst.arg & 1: + varkwarg = state.pop() + else: + varkwarg = None + vararg = state.pop() + func = state.pop() + + if PYVERSION in ((3, 11), (3, 12)): + if _is_null_temp_reg(state.peek(1)): + state.pop() # pop NULL, it's not used + elif PYVERSION in ((3, 9), (3, 10)): + pass + else: + raise NotImplementedError(PYVERSION) + + res = state.make_temp() + state.append(inst, func=func, vararg=vararg, varkwarg=varkwarg, + res=res) + state.push(res) + else: + raise NotImplementedError(PYVERSION) def _dup_topx(self, state, inst, count): orig = [state.pop() for _ in range(count)] @@ -1187,7 +1330,7 @@ class TraceRunner(object): for val in duped: state.push(val) - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): def op_CALL_INTRINSIC_1(self, state, inst): # See https://github.com/python/cpython/blob/v3.12.0rc2/Include/ # internal/pycore_intrinsics.h#L3-L17C36 @@ -1404,7 +1547,7 @@ class TraceRunner(object): pred=pred) state.push(indval) end = inst.get_jump_target() - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): # Changed in version 3.12: Up until 3.11 the iterator was # popped when it was exhausted. Now this is handled using END_FOR # op code. @@ -1490,7 +1633,7 @@ class TraceRunner(object): op_BINARY_XOR = _binaryop def op_MAKE_FUNCTION(self, state, inst, MAKE_CLOSURE=False): - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): # https://github.com/python/cpython/commit/2f180ce # name set via co_qualname name = None @@ -1500,14 +1643,19 @@ class TraceRunner(object): raise NotImplementedError(PYVERSION) code = state.pop() closure = annotations = kwdefaults = defaults = None - if inst.arg & 0x8: - closure = state.pop() - if inst.arg & 0x4: - annotations = state.pop() - if inst.arg & 0x2: - kwdefaults = state.pop() - if inst.arg & 0x1: - defaults = state.pop() + if PYVERSION in ((3, 13), ): + assert inst.arg is None + # SET_FUNCTION_ATTRIBUTE is responsible for setting + # closure, annotations, kwdefaults and defaults. + else: + if inst.arg & 0x8: + closure = state.pop() + if inst.arg & 0x4: + annotations = state.pop() + if inst.arg & 0x2: + kwdefaults = state.pop() + if inst.arg & 0x1: + defaults = state.pop() res = state.make_temp() state.append( inst, @@ -1521,6 +1669,27 @@ class TraceRunner(object): ) state.push(res) + def op_SET_FUNCTION_ATTRIBUTE(self, state, inst): + assert PYVERSION in ((3, 13), ) + make_func_stack = state.pop() + data = state.pop() + if inst.arg == 0x1: + # 0x01 a tuple of default values for positional-only and + # positional-or-keyword parameters in positional order + state.set_function_attribute(make_func_stack, defaults=data) + elif inst.arg & 0x2: + # 0x02 a tuple of strings containing parameters’ annotations + state.set_function_attribute(make_func_stack, kwdefaults=data) + elif inst.arg & 0x4: + # 0x04 a tuple of strings containing parameters’ annotations + state.set_function_attribute(make_func_stack, annotations=data) + elif inst.arg == 0x8: + # 0x08 a tuple containing cells for free variables, making a closure + state.set_function_attribute(make_func_stack, closure=data) + else: + raise AssertionError("unreachable") + state.push(make_func_stack) + def op_MAKE_CLOSURE(self, state, inst): self.op_MAKE_FUNCTION(state, inst, MAKE_CLOSURE=True) @@ -1551,7 +1720,7 @@ class TraceRunner(object): state.fork(pc=inst.next) state.fork(pc=inst.get_jump_target()) - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): def op_RERAISE(self, state, inst): # This isn't handled, but the state is set up anyway exc = state.pop() @@ -1576,7 +1745,7 @@ class TraceRunner(object): # NOTE: Please see notes in `interpreter.py` surrounding the implementation # of LOAD_METHOD and CALL_METHOD. - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): # LOAD_METHOD has become a pseudo-instruction in 3.12 pass elif PYVERSION in ((3, 11), ): @@ -1828,9 +1997,14 @@ class _State(object): return self.get_top_block('TRY') is not None def get_varname(self, inst): + """Get referenced variable name from the instruction's oparg + """ + return self.get_varname_by_arg(inst.arg) + + def get_varname_by_arg(self, oparg: int): """Get referenced variable name from the oparg """ - return self._bytecode.co_varnames[inst.arg] + return self._bytecode.co_varnames[oparg] def terminate(self): """Mark block as terminated @@ -1852,7 +2026,7 @@ class _State(object): stack.append(self.make_temp()) # Handle changes on the blockstack blockstack = list(self._blockstack) - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): # pop expired block in destination pc while blockstack: top = blockstack[-1] @@ -1940,7 +2114,21 @@ class StatePy311(_State): return self.make_temp(prefix="null$") -if PYVERSION >= (3, 11): +class StatePy313(StatePy311): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._make_func_attrs = defaultdict(dict) + + def set_function_attribute(self, make_func_res, **kwargs): + self._make_func_attrs[make_func_res].update(kwargs) + + def get_function_attributes(self, make_func_res): + return self._make_func_attrs[make_func_res] + + +if PYVERSION in ((3, 13), ): + State = StatePy313 +elif PYVERSION in ((3, 11), (3, 12)): State = StatePy311 elif PYVERSION < (3, 11): State = _State @@ -1970,8 +2158,20 @@ AdaptBlockInfo = namedtuple( def adapt_state_infos(state): + def process_function_attributes(inst_pair): + offset, data = inst_pair + inst = state._bytecode[offset] + if inst.opname == "MAKE_FUNCTION": + data.update(state.get_function_attributes(data['res'])) + return offset, data + if PYVERSION in ((3, 13), ): + insts = tuple(map(process_function_attributes, state.instructions)) + elif PYVERSION in ((3, 10), (3, 11), (3, 12)): + insts = tuple(state.instructions) + else: + raise NotImplementedError(PYVERSION) return AdaptBlockInfo( - insts=tuple(state.instructions), + insts=insts, outgoing_phis=state.outgoing_phis, blockstack=state.blockstack_initial, active_try_block=state.find_initial_try_block(), Index: numba-0.60.0/numba/core/controlflow.py =================================================================== --- numba-0.60.0.orig/numba/core/controlflow.py +++ numba-0.60.0/numba/core/controlflow.py @@ -954,7 +954,7 @@ class ControlFlowAnalysis(object): self._curblock.terminating = True self._force_new_block = True - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): def op_RETURN_CONST(self, inst): self._curblock.terminating = True self._force_new_block = True Index: numba-0.60.0/numba/core/interpreter.py =================================================================== --- numba-0.60.0.orig/numba/core/interpreter.py +++ numba-0.60.0/numba/core/interpreter.py @@ -6,7 +6,11 @@ import logging import textwrap from numba.core import errors, ir, config -from numba.core.errors import NotDefinedError, UnsupportedError, error_extras +from numba.core.errors import ( + NotDefinedError, + UnsupportedBytecodeError, + error_extras, +) from numba.core.ir_utils import get_definition, guard from numba.core.utils import (PYVERSION, BINOPS_TO_OPERATORS, INPLACE_BINOPS_TO_OPERATORS,) @@ -15,7 +19,7 @@ from numba.core.unsafe import eh from numba.cpython.unsafe.tuple import unpack_single_tuple -if PYVERSION in ((3, 12), ): +if PYVERSION in ((3, 12), (3, 13)): # Operands for CALL_INTRINSIC_1 from numba.core.byteflow import CALL_INTRINSIC_1_Operand as ci1op elif PYVERSION in ((3, 9), (3, 10), (3, 11)): @@ -108,7 +112,7 @@ def _remove_assignment_definition(old_bo func_ir._definitions[lhs].remove(rhs) already_deleted_defs[lhs].add(rhs) elif rhs not in already_deleted_defs[lhs]: - raise UnsupportedError( + raise UnsupportedBytecodeError( "Inconsistency found in the definitions while executing" " a peephole optimization. This suggests an internal" " error or inconsistency elsewhere in the compiler." @@ -211,7 +215,7 @@ def _call_function_ex_replace_kws_large( ): # We cannot handle this format so raise the # original error message. - raise UnsupportedError(errmsg) + raise UnsupportedBytecodeError(errmsg) key_var_name = const_stmt.target.name key_val = const_stmt.value.value search_start += 1 @@ -257,7 +261,7 @@ def _call_function_ex_replace_kws_large( ): # We cannot handle this format so raise the # original error message. - raise UnsupportedError(errmsg) + raise UnsupportedBytecodeError(errmsg) setitem_stmt = old_body[search_start + 1] if not ( isinstance(setitem_stmt, ir.Assign) @@ -277,7 +281,7 @@ def _call_function_ex_replace_kws_large( # getattr. If for some reason this doesn't match the code # format, we raise the original error message. This check # is meant as a precaution. - raise UnsupportedError(errmsg) + raise UnsupportedBytecodeError(errmsg) arg_var = setitem_stmt.value.args[1] # Append the (key, value) pair. kws.append((key_val, arg_var)) @@ -421,7 +425,7 @@ def _call_function_ex_replace_args_large and concat_stmt.value.fn == operator.add ): # We cannot handle this format. - raise UnsupportedError(errmsg) + raise UnsupportedBytecodeError(errmsg) lhs_name = concat_stmt.value.lhs.name rhs_name = concat_stmt.value.rhs.name # The previous statement should be a @@ -439,7 +443,7 @@ def _call_function_ex_replace_args_large and len(arg_tuple_stmt.value.items) == 1 ): # We cannot handle this format. - raise UnsupportedError(errmsg) + raise UnsupportedBytecodeError(errmsg) if arg_tuple_stmt.target.name == lhs_name: # The tuple should always be generated on the RHS. raise AssertionError("unreachable") @@ -447,7 +451,7 @@ def _call_function_ex_replace_args_large target_name = lhs_name else: # We cannot handle this format. - raise UnsupportedError(errmsg) + raise UnsupportedBytecodeError(errmsg) total_args.append( arg_tuple_stmt.value.items[0] ) @@ -497,7 +501,7 @@ def _call_function_ex_replace_args_large # If we reached the start we never found the build_tuple. # We cannot handle this format so raise the # original error message. - raise UnsupportedError(errmsg) + raise UnsupportedBytecodeError(errmsg) # Reverse the arguments so we get the correct order. return total_args[::-1] @@ -586,7 +590,7 @@ def peep_hole_call_function_ex_to_call_f # If we couldn't find where the kwargs are created # then it should be a normal **kwargs call # so we produce an unsupported message. - raise UnsupportedError(errmsg) + raise UnsupportedBytecodeError(errmsg) # Determine the kws if keyword_def.value.items: # n_kws <= 15 case. @@ -638,7 +642,7 @@ def peep_hole_call_function_ex_to_call_f if args: # If we have vararg then args is expected to # be an empty list. - raise UnsupportedError(errmsg) + raise UnsupportedBytecodeError(errmsg) vararg_loc = start_search args_def = None found = False @@ -654,7 +658,7 @@ def peep_hole_call_function_ex_to_call_f if not found: # If we couldn't find where the args are created # then we can't handle this format. - raise UnsupportedError(errmsg) + raise UnsupportedBytecodeError(errmsg) if ( isinstance(args_def.value, ir.Expr) and args_def.value.op == "build_tuple" @@ -683,7 +687,7 @@ def peep_hole_call_function_ex_to_call_f # If there is a call with vararg we need to check # if the list -> tuple conversion failed and if so # throw an error. - raise UnsupportedError(errmsg) + raise UnsupportedBytecodeError(errmsg) else: # Here the IR is an initial empty build_tuple. # Then for each arg, a new tuple with a single @@ -747,7 +751,7 @@ def peep_hole_call_function_ex_to_call_f # exception. expr = func_ir._definitions[vararg_name][0] if isinstance(expr, ir.Expr) and expr.op == "list_to_tuple": - raise UnsupportedError(errmsg) + raise UnsupportedBytecodeError(errmsg) new_body.append(stmt) # Replace the block body if we changed the IR @@ -1197,7 +1201,7 @@ def peep_hole_fuse_dict_add_updates(func else: # If we cannot remove _update_from_bytecode # Then raise an error for the user. - raise UnsupportedError(errmsg) + raise UnsupportedBytecodeError(errmsg) # Check if we need to drop any maps from being tracked. # Skip the setitem/_update_from_bytecode getattr that @@ -1385,7 +1389,7 @@ class Interpreter(object): max(inst_blocks.body)) self.last_active_offset = last_active_offset - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): self.active_exception_entries = tuple( [entry for entry in self.bytecode.exception_entries if entry.start < self.last_active_offset]) @@ -1401,7 +1405,7 @@ class Interpreter(object): # Interpret loop for inst, kws in self._iter_inst(): self._dispatch(inst, kws) - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): # Insert end of try markers self._end_try_blocks() elif PYVERSION in ((3, 9), (3, 10)): @@ -1418,12 +1422,12 @@ class Interpreter(object): # post process the IR to rewrite opcodes/byte sequences that are too # involved to risk handling as part of direct interpretation peepholes = [] - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): peepholes.append(peep_hole_split_at_pop_block) - if PYVERSION in ((3, 9), (3, 10), (3, 11), (3, 12)): + if PYVERSION in ((3, 9), (3, 10), (3, 11), (3, 12), (3, 13)): peepholes.append(peep_hole_list_to_tuple) peepholes.append(peep_hole_delete_with_exit) - if PYVERSION in ((3, 10), (3, 11), (3, 12)): + if PYVERSION in ((3, 10), (3, 11), (3, 12), (3, 13)): # peep_hole_call_function_ex_to_call_function_kw # depends on peep_hole_list_to_tuple converting # any large number of arguments from a list to a @@ -1456,7 +1460,7 @@ class Interpreter(object): See also: _insert_try_block_end """ - assert PYVERSION in ((3, 11), (3, 12)) + assert PYVERSION in ((3, 11), (3, 12), (3, 13)) graph = self.cfa.graph for offset, block in self.blocks.items(): # Get current blockstack @@ -1507,7 +1511,7 @@ class Interpreter(object): first = uservar[0] loc = self.current_scope.get(first).loc msg = "Exception object cannot be stored into variable ({})." - raise errors.UnsupportedError(msg.format(first), loc=loc) + raise errors.UnsupportedBytecodeError(msg.format(first), loc=loc) def init_first_block(self): # Define variables receiving the function arguments @@ -1564,7 +1568,7 @@ class Interpreter(object): self.dfainfo = self.dfa.infos[self.current_block_offset] self.assigner = Assigner() # Check out-of-scope syntactic-block - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): # This is recreating pre-3.11 code structure while self.syntax_blocks: if offset >= self.syntax_blocks[-1].exit: @@ -1735,7 +1739,7 @@ class Interpreter(object): val = self.get(varname) except ir.NotDefinedError: # Hack to make sure exception variables are defined - assert PYVERSION in ((3, 11), (3, 12)), \ + assert PYVERSION in ((3, 11), (3, 12), (3, 13)), \ "unexpected missing definition" val = ir.Const(value=None, loc=self.loc) stmt = ir.Assign(value=val, target=target, @@ -1795,7 +1799,7 @@ class Interpreter(object): if self._DEBUG_PRINT: print(inst) assert self.current_block is not None - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): if self.syntax_blocks: top = self.syntax_blocks[-1] if isinstance(top, ir.With) : @@ -1825,6 +1829,9 @@ class Interpreter(object): if not config.FULL_TRACEBACKS: raise err from None else: + m = f"handling op: {inst} | offset: {inst.offset}" + err.add_context(m) + err.add_context(self.bytecode.dump()) raise err # --- Scope operations --- @@ -1921,6 +1928,10 @@ class Interpreter(object): loc=self.loc) self.store(expr, st) + def op_FORMAT_SIMPLE(self, inst, value, res, strvar): + # Same as FORMAT_VALUE + return self.op_FORMAT_VALUE(inst, value, res, strvar) + def op_FORMAT_VALUE(self, inst, value, res, strvar): """ FORMAT_VALUE(flags): flags argument specifies format spec which is not @@ -1971,7 +1982,7 @@ class Interpreter(object): (), loc=self.loc) self.store(value=sliceinst, name=res) - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): def op_BINARY_SLICE(self, inst, start, end, container, res, slicevar, temp_res): start = self.get(start) @@ -1990,7 +2001,7 @@ class Interpreter(object): else: raise NotImplementedError(PYVERSION) - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): def op_STORE_SLICE(self, inst, start, end, container, value, res, slicevar): start = self.get(start) @@ -2218,11 +2229,58 @@ class Interpreter(object): stmt = ir.DelItem(base, self.get(indexvar), loc=self.loc) self.current_block.append(stmt) - def op_LOAD_FAST(self, inst, res): + def _op_LOAD_FAST(self, inst, res): srcname = self.code_locals[inst.arg] self.store(value=self.get(srcname), name=res) - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 13), ): + def op_LOAD_FAST(self, inst, res, as_load_deref=False): + if as_load_deref: + self.op_LOAD_DEREF(inst, res) + else: + self._op_LOAD_FAST(inst, res) + + else: + op_LOAD_FAST = _op_LOAD_FAST + + if PYVERSION in ((3, 13),): + def op_LOAD_FAST_LOAD_FAST(self, inst, res1, res2): + oparg = inst.arg + oparg1 = oparg >> 4 + oparg2 = oparg & 15 + src1 = self.get(self.code_locals[oparg1]) + src2 = self.get(self.code_locals[oparg2]) + self.store(value=src1, name=res1) + self.store(value=src2, name=res2) + + def op_STORE_FAST_LOAD_FAST(self, inst, store_value, load_res): + oparg = inst.arg + oparg1 = oparg >> 4 + oparg2 = oparg & 15 + + dstname = self.code_locals[oparg1] + dst_value = self.get(store_value) + self.store(value=dst_value, name=dstname) + + src_value = self.get(self.code_locals[oparg2]) + self.store(value=src_value, name=load_res) + + def op_STORE_FAST_STORE_FAST(self, inst, value1, value2): + oparg = inst.arg + oparg1 = oparg >> 4 + oparg2 = oparg & 15 + + dstname = self.code_locals[oparg1] + self.store(value=self.get(value1), name=dstname) + dstname = self.code_locals[oparg2] + self.store(value=self.get(value2), name=dstname) + + elif PYVERSION in ((3, 10), (3, 11), (3, 12)): + pass + else: + raise NotImplementedError(PYVERSION) + + if PYVERSION in ((3, 12), (3, 13)): op_LOAD_FAST_CHECK = op_LOAD_FAST def op_LOAD_FAST_AND_CLEAR(self, inst, res): @@ -2269,7 +2327,7 @@ class Interpreter(object): def op_LOAD_ATTR(self, inst, item, res): item = self.get(item) - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): attr = self.code_names[inst.arg >> 1] elif PYVERSION in ((3, 9), (3, 10), (3, 11)): attr = self.code_names[inst.arg] @@ -2300,7 +2358,7 @@ class Interpreter(object): const = ir.Const(value, loc=self.loc) self.store(const, res) - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): def op_LOAD_GLOBAL(self, inst, idx, res): name = self.code_names[idx] value = self.get_global_value(name) @@ -2318,11 +2376,15 @@ class Interpreter(object): def op_COPY_FREE_VARS(self, inst): pass - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): def op_LOAD_DEREF(self, inst, res): name = self.func_id.func.__code__._varname_from_oparg(inst.arg) if name in self.code_cellvars: - gl = self.get(name) + try: + gl = self.get(name) + except NotDefinedError: + msg = "Unsupported use of cell variable encountered" + raise NotImplementedError(msg) elif name in self.code_freevars: idx = self.code_freevars.index(name) value = self.get_closure_value(idx) @@ -2343,11 +2405,11 @@ class Interpreter(object): else: raise NotImplementedError(PYVERSION) - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): def op_MAKE_CELL(self, inst): pass # ignored bytecode - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): def op_STORE_DEREF(self, inst, value): name = self.func_id.func.__code__._varname_from_oparg(inst.arg) value = self.get(value) @@ -2387,7 +2449,7 @@ class Interpreter(object): def op_BEFORE_WITH(self, inst, contextmanager, exitfn, end): assert self.blocks[inst.offset] is self.current_block - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): # Python 3.12 hack for handling nested with blocks if end > self.last_active_offset: # Use exception entries to figure out end of syntax block @@ -2437,6 +2499,7 @@ class Interpreter(object): func = self.get(func) args = [self.get(x) for x in args] if kw_names is not None: + assert PYVERSION < (3, 13) names = self.code_consts[kw_names] kwargs = list(zip(names, args[-len(names):])) args = args[:-len(names)] @@ -2445,6 +2508,19 @@ class Interpreter(object): expr = ir.Expr.call(func, args, kwargs, loc=self.loc) self.store(expr, res) + if PYVERSION in ((3, 13),): + def op_CALL_KW(self, inst, func, args, kw_names, res): + func = self.get(func) + args = [self.get(x) for x in args] + consti = int(kw_names.rsplit('.', 2)[-1]) + names = self.code_consts[consti] + kwargs = list(zip(names, args[-len(names):])) + args = args[:-len(names)] + expr = ir.Expr.call(func, args, kwargs, loc=self.loc) + self.store(expr, res) + else: + assert PYVERSION < (3, 13) + def op_CALL_FUNCTION(self, inst, func, args, res): func = self.get(func) args = [self.get(x) for x in args] @@ -2878,6 +2954,8 @@ class Interpreter(object): jmp = ir.Jump(inst.get_jump_target(), loc=self.loc) self.current_block.append(jmp) + op_JUMP_BACKWARD_NO_INTERRUPT = op_JUMP_BACKWARD + def op_POP_BLOCK(self, inst, kind=None): if kind is None: self.syntax_blocks.pop() @@ -2892,7 +2970,7 @@ class Interpreter(object): ret = ir.Return(self.get(castval), loc=self.loc) self.current_block.append(ret) - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): def op_RETURN_CONST(self, inst, retval, castval): value = self.code_consts[inst.arg] const = ir.Const(value, loc=self.loc) @@ -2905,8 +2983,20 @@ class Interpreter(object): else: raise NotImplementedError(PYVERSION) + if PYVERSION in ((3, 13),): + def op_TO_BOOL(self, inst, val, res): + self.store(self.get(val), res) # TODO: just a lazy hack + + elif PYVERSION in ((3, 10), (3, 11), (3, 12)): + pass + else: + raise NotImplementedError(PYVERSION) + def op_COMPARE_OP(self, inst, lhs, rhs, res): - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 13),): + op = dis.cmp_op[inst.arg >> 5] + # TODO: fifth lowest bit now indicates a forced version to bool. + elif PYVERSION in ((3, 12),): op = dis.cmp_op[inst.arg >> 4] elif PYVERSION in ((3, 9), (3, 10), (3, 11)): op = dis.cmp_op[inst.arg] @@ -3024,7 +3114,7 @@ class Interpreter(object): def op_POP_JUMP_FORWARD_IF_NOT_NONE(self, inst, pred): self._jump_if_none(inst, pred, False) - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): def op_POP_JUMP_IF_NONE(self, inst, pred): self._jump_if_none(inst, pred, True) @@ -3152,7 +3242,7 @@ class Interpreter(object): "Probably caused by complex control-flow constructs; " "e.g. try-except" ) - raise errors.UnsupportedError(msg, loc=self.loc) + raise errors.UnsupportedBytecodeError(msg, loc=self.loc) fcode = assume_code_const.value if name: name = self.get(name) @@ -3166,14 +3256,14 @@ class Interpreter(object): self.op_MAKE_FUNCTION(inst, name, code, closure, annotations, kwdefaults, defaults, res) - if PYVERSION in ((3, 11), (3, 12)): + if PYVERSION in ((3, 11), (3, 12), (3, 13)): def op_LOAD_CLOSURE(self, inst, res): name = self.func_id.func.__code__._varname_from_oparg(inst.arg) if name in self.code_cellvars: try: gl = self.get(name) except NotDefinedError: - msg = "Unsupported use of op_LOAD_CLOSURE encountered" + msg = "Unsupported use of cell variable encountered" raise NotImplementedError(msg) elif name in self.code_freevars: idx = self.code_freevars.index(name) @@ -3191,7 +3281,7 @@ class Interpreter(object): try: gl = self.get(name) except NotDefinedError: - msg = "Unsupported use of op_LOAD_CLOSURE encountered" + msg = "Unsupported use of cell variable encountered" raise NotImplementedError(msg) else: idx = inst.arg - n_cellvars @@ -3228,7 +3318,7 @@ class Interpreter(object): "op_LIST_EXTEND at the start of a block.\n\nThis could be " "due to the use of a branch in a tuple unpacking statement.") if not self.current_block.body: - raise errors.UnsupportedError(msg) + raise errors.UnsupportedBytecodeError(msg) # is last emitted statement a build_tuple? stmt = self.current_block.body[-1] @@ -3258,7 +3348,7 @@ class Interpreter(object): ok = False break if ok and build_empty_list is None: - raise errors.UnsupportedError(msg) + raise errors.UnsupportedBytecodeError(msg) if ok: stmts = self.current_block.body build_tuple_asgn = self.current_block.body[-1] @@ -3304,7 +3394,7 @@ class Interpreter(object): def op_CALL_METHOD(self, *args, **kws): self.op_CALL_FUNCTION(*args, **kws) - if PYVERSION in ((3, 12), ): + if PYVERSION in ((3, 12), (3, 13)): def op_CALL_INTRINSIC_1(self, inst, operand, **kwargs): if operand == ci1op.INTRINSIC_STOPITERATION_ERROR: stmt = ir.StaticRaise(INTRINSIC_STOPITERATION_ERROR, (), @@ -3325,7 +3415,7 @@ class Interpreter(object): raise NotImplementedError(PYVERSION) -if PYVERSION in ((3, 12), ): +if PYVERSION in ((3, 12), (3, 13)): class INTRINSIC_STOPITERATION_ERROR(AssertionError): pass elif PYVERSION in ((3, 9), (3, 10), (3, 11)): Index: numba-0.60.0/numba/cpython/unicode.py =================================================================== --- numba-0.60.0.orig/numba/cpython/unicode.py +++ numba-0.60.0/numba/cpython/unicode.py @@ -349,7 +349,7 @@ def _set_code_point(a, i, ch): "Unexpected unicode representation in _set_code_point") -if PYVERSION in ((3, 12),): +if PYVERSION in ((3, 12), (3, 13)): @register_jitable def _pick_kind(kind1, kind2): if kind1 == PY_UNICODE_1BYTE_KIND: @@ -393,7 +393,7 @@ def _pick_ascii(is_ascii1, is_ascii2): return types.uint32(0) -if PYVERSION in ((3, 12),): +if PYVERSION in ((3, 12), (3, 13)): @register_jitable def _kind_to_byte_width(kind): if kind == PY_UNICODE_1BYTE_KIND: @@ -2047,7 +2047,7 @@ def _is_upper(is_lower, is_upper, is_tit def impl(a): l = len(a) if l == 1: - return is_upper(_get_code_point(a, 0)) + return is_upper(_get_code_point(a, 0)) != 0 if l == 0: return False cased = False Index: numba-0.60.0/numba/experimental/jitclass/base.py =================================================================== --- numba-0.60.0.orig/numba/experimental/jitclass/base.py +++ numba-0.60.0/numba/experimental/jitclass/base.py @@ -282,6 +282,9 @@ def _drop_ignored_attrs(dct): drop = set(['__weakref__', '__module__', '__dict__']) + if utils.PYVERSION == (3, 13): + # new in python 3.13 + drop |= set(['__firstlineno__', '__static_attributes__']) if '__annotations__' in dct: drop.add('__annotations__') @@ -300,7 +303,7 @@ def _drop_ignored_attrs(dct): drop.add('__hash__') for k in drop: - del dct[k] + dct.pop(k) class ClassBuilder(object): Index: numba-0.60.0/numba/core/compiler.py =================================================================== --- numba-0.60.0.orig/numba/core/compiler.py +++ numba-0.60.0/numba/core/compiler.py @@ -476,10 +476,7 @@ class CompilerBase(object): res = e.result break except Exception as e: - if (utils.use_new_style_errors() and not - isinstance(e, errors.NumbaError)): - raise e - + utils.handle_new_style_errors(e) self.state.status.fail_reason = e if is_final_pipeline: raise e Index: numba-0.60.0/numba/core/compiler_machinery.py =================================================================== --- numba-0.60.0.orig/numba/core/compiler_machinery.py +++ numba-0.60.0/numba/core/compiler_machinery.py @@ -304,7 +304,8 @@ class PassManager(object): args=str(internal_state.args), return_type=str(internal_state.return_type), ) - with ev.trigger_event("numba:run_pass", data=ev_details): + errctx = errors.new_error_context(f"Pass {pss.name()}") + with ev.trigger_event("numba:run_pass", data=ev_details), errctx: with SimpleTimer() as init_time: mutated |= check(pss.run_initialization, internal_state) with SimpleTimer() as pass_time: @@ -359,9 +360,7 @@ class PassManager(object): except _EarlyPipelineCompletion as e: raise e except Exception as e: - if (utils.use_new_style_errors() and not - isinstance(e, errors.NumbaError)): - raise e + utils.handle_new_style_errors(e) msg = "Failed in %s mode pipeline (step: %s)" % \ (self.pipeline_name, pass_desc) patched_exception = self._patch_error(msg, e) Index: numba-0.60.0/numba/core/errors.py =================================================================== --- numba-0.60.0.orig/numba/core/errors.py +++ numba-0.60.0/numba/core/errors.py @@ -532,7 +532,6 @@ class WarningsFixer(object): class NumbaError(Exception): - def __init__(self, msg, loc=None, highlighting=True): self.msg = msg self.loc = loc @@ -578,7 +577,13 @@ class UnsupportedError(NumbaError): """ Numba does not have an implementation for this functionality. """ - pass + + +class UnsupportedBytecodeError(Exception): + """Unsupported bytecode is non-recoverable + """ + def __init__(self, msg, loc=None): + super().__init__(f"{msg}. Raised from {loc}") class UnsupportedRewriteError(UnsupportedError): Index: numba-0.60.0/numba/core/types/functions.py =================================================================== --- numba-0.60.0.orig/numba/core/types/functions.py +++ numba-0.60.0/numba/core/types/functions.py @@ -307,12 +307,9 @@ class BaseFunction(Callable): for k, v in kws.items()} sig = temp.apply(nolitargs, nolitkws) except Exception as e: - if (utils.use_new_style_errors() and not - isinstance(e, errors.NumbaError)): - raise e - else: - sig = None - failures.add_error(temp, False, e, uselit) + utils.handle_new_style_errors(e) + sig = None + failures.add_error(temp, False, e, uselit) else: if sig is not None: self._impl_keys[sig.args] = temp.get_impl_key(sig) Index: numba-0.60.0/numba/core/utils.py =================================================================== --- numba-0.60.0.orig/numba/core/utils.py +++ numba-0.60.0/numba/core/utils.py @@ -230,6 +230,17 @@ def use_old_style_errors(): return res +def handle_new_style_errors(e): + """Handle new_style error by raising the exception immediately if they are + non-recoverable. + """ + from numba.core import errors + + if use_new_style_errors(): + if not isinstance(e, errors.NumbaError): + raise e + + class ThreadLocalStack: """A TLS stack container. Index: numba-0.60.0/numba/stencils/stencil.py =================================================================== --- numba-0.60.0.orig/numba/stencils/stencil.py +++ numba-0.60.0/numba/stencils/stencil.py @@ -402,8 +402,9 @@ class StencilFunc(object): sig = signature(real_ret, *argtys_extra) dummy_text = ("def __numba_dummy_stencil({}{}):\n pass\n".format( ",".join(self.kernel_ir.arg_names), sig_extra)) - exec(dummy_text) in globals(), locals() - dummy_func = eval("__numba_dummy_stencil") + dct = {} + exec(dummy_text, dct) + dummy_func = dct["__numba_dummy_stencil"] sig = sig.replace(pysig=utils.pysignature(dummy_func)) self._targetctx.insert_func_defn([(self._lower_me, self, argtys_extra)]) self._type_cache[argtys_extra] = (sig, result, typemap, calltypes) @@ -659,8 +660,10 @@ class StencilFunc(object): print(func_text) # Force the new stencil function into existence. - exec(func_text) in globals(), locals() - stencil_func = eval(stencil_func_name) + dct = {} + dct.update(globals()) + exec(func_text, dct) + stencil_func = dct[stencil_func_name] if sigret is not None: pysig = utils.pysignature(stencil_func) sigret.pysig = pysig Index: numba-0.60.0/numba/tests/test_debug.py =================================================================== --- numba-0.60.0.orig/numba/tests/test_debug.py +++ numba-0.60.0/numba/tests/test_debug.py @@ -73,7 +73,7 @@ class DebugTestBase(TestCase): self.assert_fails(check_meth, out) def _check_dump_bytecode(self, out): - if utils.PYVERSION in ((3, 11), (3, 12)): + if utils.PYVERSION in ((3, 11), (3, 12), (3, 13)): self.assertIn('BINARY_OP', out) elif utils.PYVERSION in ((3, 9), (3, 10)): self.assertIn('BINARY_ADD', out) Index: numba-0.60.0/numba/tests/test_ir_inlining.py =================================================================== --- numba-0.60.0.orig/numba/tests/test_ir_inlining.py +++ numba-0.60.0/numba/tests/test_ir_inlining.py @@ -444,7 +444,7 @@ class TestFunctionInlining(MemoryLeakMix return bar(z + 2) # block count changes with Python version due to bytecode differences. - if utils.PYVERSION in ((3, 12), ): + if utils.PYVERSION in ((3, 12), (3, 13)): bc = 39 elif utils.PYVERSION in ((3, 10), (3, 11)): bc = 35 Index: numba-0.60.0/numba/tests/test_closure.py =================================================================== --- numba-0.60.0.orig/numba/tests/test_closure.py +++ numba-0.60.0/numba/tests/test_closure.py @@ -400,13 +400,13 @@ class TestInlinedClosure(TestCase): with self.assertRaises(NotImplementedError) as raises: cfunc = jit(nopython=True)(outer3) cfunc(var) - msg = "Unsupported use of op_LOAD_CLOSURE encountered" + msg = "Unsupported use of cell variable encountered" self.assertIn(msg, str(raises.exception)) with self.assertRaises(NotImplementedError) as raises: cfunc = jit(nopython=True)(outer4) cfunc(var) - msg = "Unsupported use of op_LOAD_CLOSURE encountered" + msg = "Unsupported use of cell variable encountered" self.assertIn(msg, str(raises.exception)) with self.assertRaises(TypingError) as raises: Index: numba-0.60.0/numba/core/inline_closurecall.py =================================================================== --- numba-0.60.0.orig/numba/core/inline_closurecall.py +++ numba-0.60.0/numba/core/inline_closurecall.py @@ -95,7 +95,7 @@ class InlineClosureCallPass(object): modified = False work_list = list(self.func_ir.blocks.items()) debug_print = _make_debug_print("InlineClosureCallPass") - debug_print("START") + debug_print(f"START {self.func_ir.func_id.func_qualname}") while work_list: _label, block = work_list.pop() for i, instr in enumerate(block.body): Index: numba-0.60.0/numba/pycc/modulemixin.c =================================================================== --- numba-0.60.0.orig/numba/pycc/modulemixin.c +++ numba-0.60.0/numba/pycc/modulemixin.c @@ -23,6 +23,12 @@ #include "../core/runtime/nrt.h" #endif +#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION >= 12) + #define Py_BUILD_CORE 1 + #include "internal/pycore_pyhash.h" + #undef Py_BUILD_CORE +#endif + /* Defines hashsecret variables (see issue #6386) */ int64_t _numba_hashsecret_siphash_k0; int64_t _numba_hashsecret_siphash_k1; Index: numba-0.60.0/numba/tests/test_operators.py =================================================================== --- numba-0.60.0.orig/numba/tests/test_operators.py +++ numba-0.60.0/numba/tests/test_operators.py @@ -768,7 +768,7 @@ class TestOperators(TestCase): # error message depends on Python version. if utils.PYVERSION in ((3, 9),): msg = "can't mod complex numbers" - elif utils.PYVERSION in ((3, 10), (3, 11), (3, 12)): + elif utils.PYVERSION in ((3, 10), (3, 11), (3, 12), (3, 13)): msg = "unsupported operand type(s) for %" else: raise NotImplementedError(utils.PYVERSION) Index: numba-0.60.0/numba/tests/test_parfors.py =================================================================== --- numba-0.60.0.orig/numba/tests/test_parfors.py +++ numba-0.60.0/numba/tests/test_parfors.py @@ -3550,7 +3550,7 @@ class TestPrangeBase(TestParforsBase): prange_names.append('prange') prange_names = tuple(prange_names) prange_idx = len(prange_names) - 1 - if utils.PYVERSION in ((3, 11), (3, 12)): + if utils.PYVERSION in ((3, 11), (3, 12), (3, 13)): # this is the inverse of _fix_LOAD_GLOBAL_arg prange_idx = 1 + (prange_idx << 1) elif utils.PYVERSION in ((3, 9), (3, 10)): Index: numba-0.60.0/numba/tests/support.py =================================================================== --- numba-0.60.0.orig/numba/tests/support.py +++ numba-0.60.0/numba/tests/support.py @@ -128,6 +128,12 @@ def expected_failure_np2(fn): else: return fn +def expected_failure_py313(fn): + if utils.PYVERSION == (3, 13): + return unittest.expectedFailure(fn) + else: + return fn + _msg = "SciPy needed for test" skip_unless_scipy = unittest.skipIf(scipy is None, _msg) Index: numba-0.60.0/numba/tests/test_np_functions.py =================================================================== --- numba-0.60.0.orig/numba/tests/test_np_functions.py +++ numba-0.60.0/numba/tests/test_np_functions.py @@ -6192,8 +6192,9 @@ def foo(): tystr = ty.__name__ basestr = basefunc.__name__ funcstr = self.template % (tystr, basestr) - eval(compile(funcstr, '', 'exec')) - return locals()['foo'] + dct = {} + exec(compile(funcstr, '', 'exec'), globals(), dct) + return dct['foo'] @unittest.skipIf(numpy_version >= (1, 24), "NumPy < 1.24 required") def test_MachAr(self): Index: numba-0.60.0/numba/tests/test_unicode.py =================================================================== --- numba-0.60.0.orig/numba/tests/test_unicode.py +++ numba-0.60.0/numba/tests/test_unicode.py @@ -6,10 +6,12 @@ from numba import njit, typeof from numba.core import types import unittest from numba.tests.support import (TestCase, no_pyobj_flags, MemoryLeakMixin) -from numba.core.errors import TypingError, UnsupportedError +from numba.core.errors import (TypingError, UnsupportedError, + UnsupportedBytecodeError) from numba.cpython.unicode import _MAX_UNICODE from numba.core.types.functions import _header_lead from numba.extending import overload +from numba.core.utils import PYVERSION def isascii(s): @@ -2697,10 +2699,17 @@ class TestUnicodeAuxillary(BaseTest): self.assertEqual(got, expected) # check error when format spec provided - with self.assertRaises(UnsupportedError) as raises: + unsupported_errors = (UnsupportedError, UnsupportedBytecodeError) + with self.assertRaises(unsupported_errors) as raises: njit(impl4)(["A", "B"]) - msg = "format spec in f-strings not supported yet" - self.assertIn(msg, str(raises.exception)) + if PYVERSION in ((3, 13),): + msg = "Use of unsupported opcode (FORMAT_WITH_SPEC)" + self.assertIn(msg, str(raises.exception)) + elif PYVERSION in ((3, 10), (3, 11), (3, 12)): + msg = "format spec in f-strings not supported yet" + self.assertIn(msg, str(raises.exception)) + else: + raise NotImplementedError(PYVERSION) self.assertEqual(impl5(), njit(impl5)()) Index: numba-0.60.0/numba/core/ir.py =================================================================== --- numba-0.60.0.orig/numba/core/ir.py +++ numba-0.60.0/numba/core/ir.py @@ -90,9 +90,12 @@ class Loc(object): def get_lines(self): if self.lines is None: - - self.lines = linecache.getlines(self._get_path()) - + path = self._get_path() + # Avoid reading from dynamic string. They are most likely + # overridden. Problem started with Python 3.13. "" seems + # to be something from multiprocessing. + lns = [] if path == "" else linecache.getlines(path) + self.lines = lns return self.lines def _get_path(self): @@ -1496,7 +1499,7 @@ class FunctionIR(object): self.block_entry_vars = {} def derive(self, blocks, arg_count=None, arg_names=None, - force_non_generator=False): + force_non_generator=False, loc=None): """ Derive a new function IR from this one, using the given blocks, and possibly modifying the argument count and generator flag. @@ -1507,7 +1510,7 @@ class FunctionIR(object): new_ir = copy.copy(self) new_ir.blocks = blocks - new_ir.loc = firstblock.loc + new_ir.loc = firstblock.loc if loc is None else loc if force_non_generator: new_ir.is_generator = False if arg_count is not None: Index: numba-0.60.0/numba/core/transforms.py =================================================================== --- numba-0.60.0.orig/numba/core/transforms.py +++ numba-0.60.0/numba/core/transforms.py @@ -191,12 +191,20 @@ def _loop_lift_modify_blocks(func_ir, lo loopblocks = dict((k, blocks[k].copy()) for k in loopblockkeys) # Modify the loop blocks _loop_lift_prepare_loop_func(loopinfo, loopblocks) - + # Since Python 3.13, [END_FOR, POP_TOP] sequence becomes the start of the + # block causing the block to have line number of the start of previous loop. + # Fix this using the loc of the first getiter. + getiter_exprs = [] + for blk in loopblocks.values(): + getiter_exprs.extend(blk.find_exprs(op="getiter")) + first_getiter = min(getiter_exprs, key=lambda x: x.loc.line) + loop_loc = first_getiter.loc # Create a new IR for the lifted loop lifted_ir = func_ir.derive(blocks=loopblocks, arg_names=tuple(loopinfo.inputs), arg_count=len(loopinfo.inputs), - force_non_generator=True) + force_non_generator=True, + loc=loop_loc) liftedloop = LiftedLoop(lifted_ir, typingctx, targetctx, flags, locals) Index: numba-0.60.0/numba/tests/test_exceptions.py =================================================================== --- numba-0.60.0.orig/numba/tests/test_exceptions.py +++ numba-0.60.0/numba/tests/test_exceptions.py @@ -6,6 +6,7 @@ from numba import jit, njit from numba.core import types, errors, utils from numba.tests.support import (TestCase, expected_failure_py311, expected_failure_py312, + expected_failure_py313, ) import unittest @@ -440,6 +441,7 @@ class TestRaising(TestCase): @expected_failure_py311 @expected_failure_py312 + @expected_failure_py313 def test_dynamic_raise(self): @njit Index: numba-0.60.0/numba/tests/test_try_except.py =================================================================== --- numba-0.60.0.orig/numba/tests/test_try_except.py +++ numba-0.60.0/numba/tests/test_try_except.py @@ -8,12 +8,13 @@ from numba import njit, typed, objmode, from numba.core.utils import PYVERSION from numba.core import ir_utils, ir from numba.core.errors import ( - UnsupportedError, CompilerError, NumbaPerformanceWarning, TypingError, + CompilerError, NumbaPerformanceWarning, TypingError, + UnsupportedBytecodeError, ) from numba.tests.support import ( TestCase, unittest, captured_stdout, MemoryLeakMixin, skip_parfors_unsupported, skip_unless_scipy, expected_failure_py311, - expected_failure_py312 + expected_failure_py312, expected_failure_py313, ) @@ -372,7 +373,7 @@ class TestTryBareExcept(TestCase): except: # noqa: E722 raise - with self.assertRaises(UnsupportedError) as raises: + with self.assertRaises(UnsupportedBytecodeError) as raises: udt() self.assertIn( "The re-raising of an exception is not yet supported.", @@ -459,7 +460,7 @@ class TestTryExceptCaught(TestCase): return r return r - with self.assertRaises(UnsupportedError) as raises: + with self.assertRaises(UnsupportedBytecodeError) as raises: udt(True) self.assertIn( "Exception object cannot be stored into variable (e)", @@ -474,7 +475,7 @@ class TestTryExceptCaught(TestCase): except Exception: raise - with self.assertRaises(UnsupportedError) as raises: + with self.assertRaises(UnsupportedBytecodeError) as raises: udt() self.assertIn( "The re-raising of an exception is not yet supported.", @@ -492,7 +493,7 @@ class TestTryExceptCaught(TestCase): except Exception: raise - with self.assertRaises(UnsupportedError) as raises: + with self.assertRaises(UnsupportedBytecodeError) as raises: udt() self.assertIn( "The re-raising of an exception is not yet supported.", @@ -692,6 +693,7 @@ class TestTryExceptOtherControlFlow(Test @expected_failure_py311 @expected_failure_py312 + @expected_failure_py313 def test_objmode(self): @njit def udt(): @@ -712,6 +714,7 @@ class TestTryExceptOtherControlFlow(Test @expected_failure_py311 @expected_failure_py312 + @expected_failure_py313 def test_objmode_output_type(self): def bar(x): return np.asarray(list(reversed(x.tolist()))) Index: numba-0.60.0/numba/tests/test_withlifting.py =================================================================== --- numba-0.60.0.orig/numba/tests/test_withlifting.py +++ numba-0.60.0/numba/tests/test_withlifting.py @@ -16,7 +16,8 @@ from numba.tests.support import (MemoryL skip_unless_scipy, linux_only, strace_supported, strace, expected_failure_py311, - expected_failure_py312) + expected_failure_py312, + expected_failure_py313) from numba.core.utils import PYVERSION from numba.experimental import jitclass import unittest @@ -280,6 +281,7 @@ class TestLiftCall(BaseTestWithLifting): @expected_failure_py311 @expected_failure_py312 + @expected_failure_py313 def test_liftcall5(self): self.check_extracted_with(liftcall5, expect_count=1, expected_stdout="0\n1\n2\n3\n4\n5\nA\n") @@ -719,6 +721,7 @@ class TestLiftObj(MemoryLeak, TestCase): @expected_failure_py311 @expected_failure_py312 + @expected_failure_py313 def test_case19_recursion(self): def foo(x): with objmode_context(): @@ -1169,7 +1172,7 @@ class TestBogusContext(BaseTestWithLifti with open('') as f: pass - with self.assertRaises(errors.UnsupportedError) as raises: + with self.assertRaises(errors.UnsupportedBytecodeError) as raises: foo() excstr = str(raises.exception) Index: numba-0.60.0/numba/tests/test_sys_monitoring.py =================================================================== --- numba-0.60.0.orig/numba/tests/test_sys_monitoring.py +++ numba-0.60.0/numba/tests/test_sys_monitoring.py @@ -4,7 +4,7 @@ import sys import threading import unittest from unittest.mock import Mock, call -from numba.tests.support import TestCase, skip_unless_py312 +from numba.tests.support import TestCase from numba import jit, objmode from numba.core.utils import PYVERSION from numba.core.serialize import _numba_unpickle @@ -21,7 +21,7 @@ def generate_usecase(): return foo, call_foo -if PYVERSION == (3, 12): +if PYVERSION in ((3, 12), (3, 13)): PY_START = sys.monitoring.events.PY_START PY_RETURN = sys.monitoring.events.PY_RETURN RAISE = sys.monitoring.events.RAISE @@ -36,7 +36,7 @@ TOOL2MONITORTYPE = {0 : "Debugger", 5 : "Optimizer"} -@skip_unless_py312 +@unittest.skipUnless(PYVERSION >= (3, 12), "needs Python 3.12+") class TestMonitoring(TestCase): # Tests the interaction of the Numba dispatcher with `sys.monitoring`. # @@ -724,7 +724,7 @@ class TestMonitoring(TestCase): self.assertFalse(q2.qsize()) -@skip_unless_py312 +@unittest.skipUnless(PYVERSION >= (3, 12), "needs Python 3.12+") class TestMonitoringSelfTest(TestCase): def test_skipping_of_tests_if_monitoring_in_use(self): Index: numba-0.60.0/numba/_random.c =================================================================== --- numba-0.60.0.orig/numba/_random.c +++ numba-0.60.0/numba/_random.c @@ -195,7 +195,7 @@ rnd_implicit_init(rnd_state_t *state) Py_buffer buf; PyGILState_STATE gilstate = PyGILState_Ensure(); - module = PyImport_ImportModuleNoBlock("os"); + module = PyImport_ImportModule("os"); if (module == NULL) goto error; /* Read as many bytes as necessary to get the full entropy Index: numba-0.60.0/numba/core/pythonapi.py =================================================================== --- numba-0.60.0.orig/numba/core/pythonapi.py +++ numba-0.60.0/numba/core/pythonapi.py @@ -919,9 +919,9 @@ class PythonAPI(object): # Other APIs (organize them better!) # - def import_module_noblock(self, modname): + def import_module(self, modname): fnty = ir.FunctionType(self.pyobj, [self.cstring]) - fn = self._get_function(fnty, name="PyImport_ImportModuleNoBlock") + fn = self._get_function(fnty, name="PyImport_ImportModule") return self.builder.call(fn, [modname]) def call_function_objargs(self, callee, objargs): Index: numba-0.60.0/numba/experimental/function_type.py =================================================================== --- numba-0.60.0.orig/numba/experimental/function_type.py +++ numba-0.60.0/numba/experimental/function_type.py @@ -181,7 +181,7 @@ def lower_get_wrapper_address(context, b # caller. modname = context.insert_const_string(builder.module, __name__) - numba_mod = pyapi.import_module_noblock(modname) + numba_mod = pyapi.import_module(modname) numba_func = pyapi.object_getattr_string( numba_mod, '_get_wrapper_address') pyapi.decref(numba_mod) @@ -263,3 +263,4 @@ def lower_cast_dispatcher_to_function_ty llty = context.get_value_type(types.voidptr) sfunc.pyaddr = builder.ptrtoint(val, llty) return sfunc._getvalue() + Index: numba-0.60.0/numba/typed/typeddict.py =================================================================== --- numba-0.60.0.orig/numba/typed/typeddict.py +++ numba-0.60.0/numba/typed/typeddict.py @@ -266,7 +266,7 @@ def box_dicttype(typ, val, c): modname = c.context.insert_const_string( c.builder.module, 'numba.typed.typeddict', ) - typeddict_mod = c.pyapi.import_module_noblock(modname) + typeddict_mod = c.pyapi.import_module(modname) fmp_fn = c.pyapi.object_getattr_string(typeddict_mod, '_from_meminfo_ptr') dicttype_obj = c.pyapi.unserialize(c.pyapi.serialize_object(typ)) Index: numba-0.60.0/numba/typed/typedlist.py =================================================================== --- numba-0.60.0.orig/numba/typed/typedlist.py +++ numba-0.60.0/numba/typed/typedlist.py @@ -471,7 +471,7 @@ def box_lsttype(typ, val, c): modname = c.context.insert_const_string( c.builder.module, 'numba.typed.typedlist', ) - typedlist_mod = c.pyapi.import_module_noblock(modname) + typedlist_mod = c.pyapi.import_module(modname) fmp_fn = c.pyapi.object_getattr_string(typedlist_mod, '_from_meminfo_ptr') lsttype_obj = c.pyapi.unserialize(c.pyapi.serialize_object(typ)) Index: numba-0.60.0/numba/tests/test_interpreter.py =================================================================== --- numba-0.60.0.orig/numba/tests/test_interpreter.py +++ numba-0.60.0/numba/tests/test_interpreter.py @@ -5,7 +5,7 @@ import unittest from numba import jit, njit, objmode, typeof, literally from numba.extending import overload from numba.core import types -from numba.core.errors import UnsupportedError +from numba.core.errors import UnsupportedBytecodeError from numba.tests.support import ( TestCase, MemoryLeakMixin, @@ -388,7 +388,7 @@ class TestCallFunctionExPeepHole(MemoryL arg41=1, ) - with self.assertRaises(UnsupportedError) as raises: + with self.assertRaises(UnsupportedBytecodeError) as raises: njit()(inline_func)(False) self.assertIn( 'You can resolve this issue by moving the control flow out', @@ -498,7 +498,7 @@ class TestCallFunctionExPeepHole(MemoryL 1, ) - with self.assertRaises(UnsupportedError) as raises: + with self.assertRaises(UnsupportedBytecodeError) as raises: njit()(inline_func)(False) self.assertIn( 'You can resolve this issue by moving the control flow out', @@ -585,7 +585,7 @@ class TestCallFunctionExPeepHole(MemoryL arg15=1 if flag else 2, ) - with self.assertRaises(UnsupportedError) as raises: + with self.assertRaises(UnsupportedBytecodeError) as raises: njit()(inline_func)(False) self.assertIn( 'You can resolve this issue by moving the control flow out', @@ -973,7 +973,7 @@ class TestLargeConstDict(TestCase, Memor } return d["S"] - with self.assertRaises(UnsupportedError) as raises: + with self.assertRaises(UnsupportedBytecodeError) as raises: njit()(inline_func)("a_string", False) self.assertIn( 'You can resolve this issue by moving the control flow out', Index: numba-0.60.0/numba/tests/test_tuples.py =================================================================== --- numba-0.60.0.orig/numba/tests/test_tuples.py +++ numba-0.60.0/numba/tests/test_tuples.py @@ -731,7 +731,7 @@ class TestTupleBuild(TestCase): b = (3,2, 4) return (*(b if a[0] else (5, 6)),) - with self.assertRaises(errors.UnsupportedError) as raises: + with self.assertRaises(errors.UnsupportedBytecodeError) as raises: foo() msg = "op_LIST_EXTEND at the start of a block" self.assertIn(msg, str(raises.exception)) Index: numba-0.60.0/setup.py =================================================================== --- numba-0.60.0.orig/setup.py +++ numba-0.60.0/setup.py @@ -20,7 +20,7 @@ except ImportError: min_python_version = "3.9" -max_python_version = "3.13" # exclusive +max_python_version = "3.14" # exclusive min_numpy_build_version = "2.0.0rc1" min_numpy_run_version = "1.22" max_numpy_run_version = "2.1" Index: numba-0.60.0/numba/core/boxing.py =================================================================== --- numba-0.60.0.orig/numba/core/boxing.py +++ numba-0.60.0/numba/core/boxing.py @@ -655,7 +655,7 @@ class _NumbaTypeHelper(object): def __enter__(self): c = self.c numba_name = c.context.insert_const_string(c.builder.module, 'numba') - numba_mod = c.pyapi.import_module_noblock(numba_name) + numba_mod = c.pyapi.import_module(numba_name) typeof_fn = c.pyapi.object_getattr_string(numba_mod, 'typeof') self.typeof_fn = typeof_fn c.pyapi.decref(numba_mod) @@ -1213,7 +1213,7 @@ def unbox_numpy_random_bitgenerator(typ, # store the results. # First find ctypes.cast, and ctypes.c_void_p ctypes_name = c.context.insert_const_string(c.builder.module, 'ctypes') - ctypes_module = c.pyapi.import_module_noblock(ctypes_name) + ctypes_module = c.pyapi.import_module(ctypes_name) extra_refs.append(ctypes_module) with cgutils.early_exit_if_null(c.builder, stack, ctypes_module): handle_failure() Index: numba-0.60.0/numba/pythoncapi_compat.h =================================================================== --- /dev/null +++ numba-0.60.0/numba/pythoncapi_compat.h @@ -0,0 +1,1696 @@ +// Header file providing new C API functions to old Python versions. +// +// File distributed under the Zero Clause BSD (0BSD) license. +// Copyright Contributors to the pythoncapi_compat project. +// +// Homepage: +// https://github.com/python/pythoncapi_compat +// +// Latest version: +// https://raw.githubusercontent.com/python/pythoncapi-compat/0041177c4f348c8952b4c8980b2c90856e61c7c7/pythoncapi_compat.h +// +// SPDX-License-Identifier: 0BSD + +#ifndef PYTHONCAPI_COMPAT +#define PYTHONCAPI_COMPAT + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +// Python 3.11.0b4 added PyFrame_Back() to Python.h +#if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION) +# include "frameobject.h" // PyFrameObject, PyFrame_GetBack() +#endif + + +#ifndef _Py_CAST +# define _Py_CAST(type, expr) ((type)(expr)) +#endif + +// Static inline functions should use _Py_NULL rather than using directly NULL +// to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer, +// _Py_NULL is defined as nullptr. +#if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \ + || (defined(__cplusplus) && __cplusplus >= 201103) +# define _Py_NULL nullptr +#else +# define _Py_NULL NULL +#endif + +// Cast argument to PyObject* type. +#ifndef _PyObject_CAST +# define _PyObject_CAST(op) _Py_CAST(PyObject*, op) +#endif + +#ifndef Py_BUILD_ASSERT +# define Py_BUILD_ASSERT(cond) \ + do { \ + (void)sizeof(char [1 - 2 * !(cond)]); \ + } while(0) +#endif + + +// bpo-42262 added Py_NewRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef) +static inline PyObject* _Py_NewRef(PyObject *obj) +{ + Py_INCREF(obj); + return obj; +} +#define Py_NewRef(obj) _Py_NewRef(_PyObject_CAST(obj)) +#endif + + +// bpo-42262 added Py_XNewRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_XNewRef) +static inline PyObject* _Py_XNewRef(PyObject *obj) +{ + Py_XINCREF(obj); + return obj; +} +#define Py_XNewRef(obj) _Py_XNewRef(_PyObject_CAST(obj)) +#endif + + +// bpo-39573 added Py_SET_REFCNT() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_REFCNT) +static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) +{ + ob->ob_refcnt = refcnt; +} +#define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt) +#endif + + +// Py_SETREF() and Py_XSETREF() were added to Python 3.5.2. +// It is excluded from the limited C API. +#if (PY_VERSION_HEX < 0x03050200 && !defined(Py_SETREF)) && !defined(Py_LIMITED_API) +#define Py_SETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_DECREF(_tmp_dst); \ + } while (0) + +#define Py_XSETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_XDECREF(_tmp_dst); \ + } while (0) +#endif + + +// bpo-43753 added Py_Is(), Py_IsNone(), Py_IsTrue() and Py_IsFalse() +// to Python 3.10.0b1. +#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_Is) +# define Py_Is(x, y) ((x) == (y)) +#endif +#if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsNone) +# define Py_IsNone(x) Py_Is(x, Py_None) +#endif +#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsTrue) +# define Py_IsTrue(x) Py_Is(x, Py_True) +#endif +#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsFalse) +# define Py_IsFalse(x) Py_Is(x, Py_False) +#endif + + +// bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) +static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) +{ + ob->ob_type = type; +} +#define Py_SET_TYPE(ob, type) _Py_SET_TYPE(_PyObject_CAST(ob), type) +#endif + + +// bpo-39573 added Py_SET_SIZE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE) +static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) +{ + ob->ob_size = size; +} +#define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject*)(ob), size) +#endif + + +// bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 || defined(PYPY_VERSION) +static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) +{ + assert(frame != _Py_NULL); + assert(frame->f_code != _Py_NULL); + return _Py_CAST(PyCodeObject*, Py_NewRef(frame->f_code)); +} +#endif + +static inline PyCodeObject* _PyFrame_GetCodeBorrow(PyFrameObject *frame) +{ + PyCodeObject *code = PyFrame_GetCode(frame); + Py_DECREF(code); + return code; +} + + +// bpo-40421 added PyFrame_GetBack() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +static inline PyFrameObject* PyFrame_GetBack(PyFrameObject *frame) +{ + assert(frame != _Py_NULL); + return _Py_CAST(PyFrameObject*, Py_XNewRef(frame->f_back)); +} +#endif + +#if !defined(PYPY_VERSION) +static inline PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame) +{ + PyFrameObject *back = PyFrame_GetBack(frame); + Py_XDECREF(back); + return back; +} +#endif + + +// bpo-40421 added PyFrame_GetLocals() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetLocals(PyFrameObject *frame) +{ +#if PY_VERSION_HEX >= 0x030400B1 + if (PyFrame_FastToLocalsWithError(frame) < 0) { + return NULL; + } +#else + PyFrame_FastToLocals(frame); +#endif + return Py_NewRef(frame->f_locals); +} +#endif + + +// bpo-40421 added PyFrame_GetGlobals() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetGlobals(PyFrameObject *frame) +{ + return Py_NewRef(frame->f_globals); +} +#endif + + +// bpo-40421 added PyFrame_GetBuiltins() to Python 3.11.0a7 +#if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) +{ + return Py_NewRef(frame->f_builtins); +} +#endif + + +// bpo-40421 added PyFrame_GetLasti() to Python 3.11.0b1 +#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) +static inline int PyFrame_GetLasti(PyFrameObject *frame) +{ +#if PY_VERSION_HEX >= 0x030A00A7 + // bpo-27129: Since Python 3.10.0a7, f_lasti is an instruction offset, + // not a bytes offset anymore. Python uses 16-bit "wordcode" (2 bytes) + // instructions. + if (frame->f_lasti < 0) { + return -1; + } + return frame->f_lasti * 2; +#else + return frame->f_lasti; +#endif +} +#endif + + +// gh-91248 added PyFrame_GetVar() to Python 3.12.0a2 +#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +static inline PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name) +{ + PyObject *locals, *value; + + locals = PyFrame_GetLocals(frame); + if (locals == NULL) { + return NULL; + } +#if PY_VERSION_HEX >= 0x03000000 + value = PyDict_GetItemWithError(locals, name); +#else + value = _PyDict_GetItemWithError(locals, name); +#endif + Py_DECREF(locals); + + if (value == NULL) { + if (PyErr_Occurred()) { + return NULL; + } +#if PY_VERSION_HEX >= 0x03000000 + PyErr_Format(PyExc_NameError, "variable %R does not exist", name); +#else + PyErr_SetString(PyExc_NameError, "variable does not exist"); +#endif + return NULL; + } + return Py_NewRef(value); +} +#endif + + +// gh-91248 added PyFrame_GetVarString() to Python 3.12.0a2 +#if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) +static inline PyObject* +PyFrame_GetVarString(PyFrameObject *frame, const char *name) +{ + PyObject *name_obj, *value; +#if PY_VERSION_HEX >= 0x03000000 + name_obj = PyUnicode_FromString(name); +#else + name_obj = PyString_FromString(name); +#endif + if (name_obj == NULL) { + return NULL; + } + value = PyFrame_GetVar(frame, name_obj); + Py_DECREF(name_obj); + return value; +} +#endif + + +// bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) +static inline PyInterpreterState * +PyThreadState_GetInterpreter(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return tstate->interp; +} +#endif + + +// bpo-40429 added PyThreadState_GetFrame() to Python 3.9.0b1 +#if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) +static inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return _Py_CAST(PyFrameObject *, Py_XNewRef(tstate->frame)); +} +#endif + +#if !defined(PYPY_VERSION) +static inline PyFrameObject* +_PyThreadState_GetFrameBorrow(PyThreadState *tstate) +{ + PyFrameObject *frame = PyThreadState_GetFrame(tstate); + Py_XDECREF(frame); + return frame; +} +#endif + + +// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) +static inline PyInterpreterState* PyInterpreterState_Get(void) +{ + PyThreadState *tstate; + PyInterpreterState *interp; + + tstate = PyThreadState_GET(); + if (tstate == _Py_NULL) { + Py_FatalError("GIL released (tstate is NULL)"); + } + interp = tstate->interp; + if (interp == _Py_NULL) { + Py_FatalError("no current interpreter"); + } + return interp; +} +#endif + + +// bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a6 +#if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) +static inline uint64_t PyThreadState_GetID(PyThreadState *tstate) +{ + assert(tstate != _Py_NULL); + return tstate->id; +} +#endif + +// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2 +#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +static inline void PyThreadState_EnterTracing(PyThreadState *tstate) +{ + tstate->tracing++; +#if PY_VERSION_HEX >= 0x030A00A1 + tstate->cframe->use_tracing = 0; +#else + tstate->use_tracing = 0; +#endif +} +#endif + +// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2 +#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +static inline void PyThreadState_LeaveTracing(PyThreadState *tstate) +{ + int use_tracing = (tstate->c_tracefunc != _Py_NULL + || tstate->c_profilefunc != _Py_NULL); + tstate->tracing--; +#if PY_VERSION_HEX >= 0x030A00A1 + tstate->cframe->use_tracing = use_tracing; +#else + tstate->use_tracing = use_tracing; +#endif +} +#endif + + +// bpo-37194 added PyObject_CallNoArgs() to Python 3.9.0a1 +// PyObject_CallNoArgs() added to PyPy 3.9.16-v7.3.11 +#if !defined(PyObject_CallNoArgs) && PY_VERSION_HEX < 0x030900A1 +static inline PyObject* PyObject_CallNoArgs(PyObject *func) +{ + return PyObject_CallFunctionObjArgs(func, NULL); +} +#endif + + +// bpo-39245 made PyObject_CallOneArg() public (previously called +// _PyObject_CallOneArg) in Python 3.9.0a4 +// PyObject_CallOneArg() added to PyPy 3.9.16-v7.3.11 +#if !defined(PyObject_CallOneArg) && PY_VERSION_HEX < 0x030900A4 +static inline PyObject* PyObject_CallOneArg(PyObject *func, PyObject *arg) +{ + return PyObject_CallFunctionObjArgs(func, arg, NULL); +} +#endif + + +// bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3 +#if PY_VERSION_HEX < 0x030A00A3 +static inline int +PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) +{ + int res; + + if (!value && !PyErr_Occurred()) { + // PyModule_AddObject() raises TypeError in this case + PyErr_SetString(PyExc_SystemError, + "PyModule_AddObjectRef() must be called " + "with an exception raised if value is NULL"); + return -1; + } + + Py_XINCREF(value); + res = PyModule_AddObject(module, name, value); + if (res < 0) { + Py_XDECREF(value); + } + return res; +} +#endif + + +// bpo-40024 added PyModule_AddType() to Python 3.9.0a5 +#if PY_VERSION_HEX < 0x030900A5 +static inline int PyModule_AddType(PyObject *module, PyTypeObject *type) +{ + const char *name, *dot; + + if (PyType_Ready(type) < 0) { + return -1; + } + + // inline _PyType_Name() + name = type->tp_name; + assert(name != _Py_NULL); + dot = strrchr(name, '.'); + if (dot != _Py_NULL) { + name = dot + 1; + } + + return PyModule_AddObjectRef(module, name, _PyObject_CAST(type)); +} +#endif + + +// bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6. +// bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2. +#if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) +static inline int PyObject_GC_IsTracked(PyObject* obj) +{ + return (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)); +} +#endif + +// bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6. +// bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final. +#if PY_VERSION_HEX < 0x030900A6 && PY_VERSION_HEX >= 0x030400F0 && !defined(PYPY_VERSION) +static inline int PyObject_GC_IsFinalized(PyObject *obj) +{ + PyGC_Head *gc = _Py_CAST(PyGC_Head*, obj) - 1; + return (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(gc)); +} +#endif + + +// bpo-39573 added Py_IS_TYPE() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE) +static inline int _Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { + return Py_TYPE(ob) == type; +} +#define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST(ob), type) +#endif + + +// bpo-46906 added PyFloat_Pack2() and PyFloat_Unpack2() to Python 3.11a7. +// bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1. +// Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal +// C API: Python 3.11a2-3.11a6 versions are not supported. +#if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) +static inline int PyFloat_Pack2(double x, char *p, int le) +{ return _PyFloat_Pack2(x, (unsigned char*)p, le); } + +static inline double PyFloat_Unpack2(const char *p, int le) +{ return _PyFloat_Unpack2((const unsigned char *)p, le); } +#endif + + +// bpo-46906 added PyFloat_Pack4(), PyFloat_Pack8(), PyFloat_Unpack4() and +// PyFloat_Unpack8() to Python 3.11a7. +// Python 3.11a2 moved _PyFloat_Pack4(), _PyFloat_Pack8(), _PyFloat_Unpack4() +// and _PyFloat_Unpack8() to the internal C API: Python 3.11a2-3.11a6 versions +// are not supported. +#if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) +static inline int PyFloat_Pack4(double x, char *p, int le) +{ return _PyFloat_Pack4(x, (unsigned char*)p, le); } + +static inline int PyFloat_Pack8(double x, char *p, int le) +{ return _PyFloat_Pack8(x, (unsigned char*)p, le); } + +static inline double PyFloat_Unpack4(const char *p, int le) +{ return _PyFloat_Unpack4((const unsigned char *)p, le); } + +static inline double PyFloat_Unpack8(const char *p, int le) +{ return _PyFloat_Unpack8((const unsigned char *)p, le); } +#endif + + +// gh-92154 added PyCode_GetCode() to Python 3.11.0b1 +#if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetCode(PyCodeObject *code) +{ + return Py_NewRef(code->co_code); +} +#endif + + +// gh-95008 added PyCode_GetVarnames() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetVarnames(PyCodeObject *code) +{ + return Py_NewRef(code->co_varnames); +} +#endif + +// gh-95008 added PyCode_GetFreevars() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetFreevars(PyCodeObject *code) +{ + return Py_NewRef(code->co_freevars); +} +#endif + +// gh-95008 added PyCode_GetCellvars() to Python 3.11.0rc1 +#if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) +static inline PyObject* PyCode_GetCellvars(PyCodeObject *code) +{ + return Py_NewRef(code->co_cellvars); +} +#endif + + +// Py_UNUSED() was added to Python 3.4.0b2. +#if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED) +# if defined(__GNUC__) || defined(__clang__) +# define Py_UNUSED(name) _unused_ ## name __attribute__((unused)) +# else +# define Py_UNUSED(name) _unused_ ## name +# endif +#endif + + +// gh-105922 added PyImport_AddModuleRef() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A0 +static inline PyObject* PyImport_AddModuleRef(const char *name) +{ + return Py_XNewRef(PyImport_AddModule(name)); +} +#endif + + +// gh-105927 added PyWeakref_GetRef() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D0000 +static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) +{ + PyObject *obj; + if (ref != NULL && !PyWeakref_Check(ref)) { + *pobj = NULL; + PyErr_SetString(PyExc_TypeError, "expected a weakref"); + return -1; + } + obj = PyWeakref_GetObject(ref); + if (obj == NULL) { + // SystemError if ref is NULL + *pobj = NULL; + return -1; + } + if (obj == Py_None) { + *pobj = NULL; + return 0; + } + *pobj = Py_NewRef(obj); + return (*pobj != NULL); +} +#endif + + +// bpo-36974 added PY_VECTORCALL_ARGUMENTS_OFFSET to Python 3.8b1 +#ifndef PY_VECTORCALL_ARGUMENTS_OFFSET +# define PY_VECTORCALL_ARGUMENTS_OFFSET (_Py_CAST(size_t, 1) << (8 * sizeof(size_t) - 1)) +#endif + +// bpo-36974 added PyVectorcall_NARGS() to Python 3.8b1 +#if PY_VERSION_HEX < 0x030800B1 +static inline Py_ssize_t PyVectorcall_NARGS(size_t n) +{ + return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET; +} +#endif + + +// gh-105922 added PyObject_Vectorcall() to Python 3.9.0a4 +#if PY_VERSION_HEX < 0x030900A4 +static inline PyObject* +PyObject_Vectorcall(PyObject *callable, PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ +#if PY_VERSION_HEX >= 0x030800B1 && !defined(PYPY_VERSION) + // bpo-36974 added _PyObject_Vectorcall() to Python 3.8.0b1 + return _PyObject_Vectorcall(callable, args, nargsf, kwnames); +#else + PyObject *posargs = NULL, *kwargs = NULL; + PyObject *res; + Py_ssize_t nposargs, nkwargs, i; + + if (nargsf != 0 && args == NULL) { + PyErr_BadInternalCall(); + goto error; + } + if (kwnames != NULL && !PyTuple_Check(kwnames)) { + PyErr_BadInternalCall(); + goto error; + } + + nposargs = (Py_ssize_t)PyVectorcall_NARGS(nargsf); + if (kwnames) { + nkwargs = PyTuple_GET_SIZE(kwnames); + } + else { + nkwargs = 0; + } + + posargs = PyTuple_New(nposargs); + if (posargs == NULL) { + goto error; + } + if (nposargs) { + for (i=0; i < nposargs; i++) { + PyTuple_SET_ITEM(posargs, i, Py_NewRef(*args)); + args++; + } + } + + if (nkwargs) { + kwargs = PyDict_New(); + if (kwargs == NULL) { + goto error; + } + + for (i = 0; i < nkwargs; i++) { + PyObject *key = PyTuple_GET_ITEM(kwnames, i); + PyObject *value = *args; + args++; + if (PyDict_SetItem(kwargs, key, value) < 0) { + goto error; + } + } + } + else { + kwargs = NULL; + } + + res = PyObject_Call(callable, posargs, kwargs); + Py_DECREF(posargs); + Py_XDECREF(kwargs); + return res; + +error: + Py_DECREF(posargs); + Py_XDECREF(kwargs); + return NULL; +#endif +} +#endif + + +// gh-106521 added PyObject_GetOptionalAttr() and +// PyObject_GetOptionalAttrString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, PyObject **result) +{ + // bpo-32571 added _PyObject_LookupAttr() to Python 3.7.0b1 +#if PY_VERSION_HEX >= 0x030700B1 && !defined(PYPY_VERSION) + return _PyObject_LookupAttr(obj, attr_name, result); +#else + *result = PyObject_GetAttr(obj, attr_name); + if (*result != NULL) { + return 1; + } + if (!PyErr_Occurred()) { + return 0; + } + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + return 0; + } + return -1; +#endif +} + +static inline int +PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject **result) +{ + PyObject *name_obj; + int rc; +#if PY_VERSION_HEX >= 0x03000000 + name_obj = PyUnicode_FromString(attr_name); +#else + name_obj = PyString_FromString(attr_name); +#endif + if (name_obj == NULL) { + *result = NULL; + return -1; + } + rc = PyObject_GetOptionalAttr(obj, name_obj, result); + Py_DECREF(name_obj); + return rc; +} +#endif + + +// gh-106307 added PyObject_GetOptionalAttr() and +// PyMapping_GetOptionalItemString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result) +{ + *result = PyObject_GetItem(obj, key); + if (*result) { + return 1; + } + if (!PyErr_ExceptionMatches(PyExc_KeyError)) { + return -1; + } + PyErr_Clear(); + return 0; +} + +static inline int +PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **result) +{ + PyObject *key_obj; + int rc; +#if PY_VERSION_HEX >= 0x03000000 + key_obj = PyUnicode_FromString(key); +#else + key_obj = PyString_FromString(key); +#endif + if (key_obj == NULL) { + *result = NULL; + return -1; + } + rc = PyMapping_GetOptionalItem(obj, key_obj, result); + Py_DECREF(key_obj); + return rc; +} +#endif + +// gh-108511 added PyMapping_HasKeyWithError() and +// PyMapping_HasKeyStringWithError() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyMapping_HasKeyWithError(PyObject *obj, PyObject *key) +{ + PyObject *res; + int rc = PyMapping_GetOptionalItem(obj, key, &res); + Py_XDECREF(res); + return rc; +} + +static inline int +PyMapping_HasKeyStringWithError(PyObject *obj, const char *key) +{ + PyObject *res; + int rc = PyMapping_GetOptionalItemString(obj, key, &res); + Py_XDECREF(res); + return rc; +} +#endif + + +// gh-108511 added PyObject_HasAttrWithError() and +// PyObject_HasAttrStringWithError() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyObject_HasAttrWithError(PyObject *obj, PyObject *attr) +{ + PyObject *res; + int rc = PyObject_GetOptionalAttr(obj, attr, &res); + Py_XDECREF(res); + return rc; +} + +static inline int +PyObject_HasAttrStringWithError(PyObject *obj, const char *attr) +{ + PyObject *res; + int rc = PyObject_GetOptionalAttrString(obj, attr, &res); + Py_XDECREF(res); + return rc; +} +#endif + + +// gh-106004 added PyDict_GetItemRef() and PyDict_GetItemStringRef() +// to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **result) +{ +#if PY_VERSION_HEX >= 0x03000000 + PyObject *item = PyDict_GetItemWithError(mp, key); +#else + PyObject *item = _PyDict_GetItemWithError(mp, key); +#endif + if (item != NULL) { + *result = Py_NewRef(item); + return 1; // found + } + if (!PyErr_Occurred()) { + *result = NULL; + return 0; // not found + } + *result = NULL; + return -1; +} + +static inline int +PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result) +{ + int res; +#if PY_VERSION_HEX >= 0x03000000 + PyObject *key_obj = PyUnicode_FromString(key); +#else + PyObject *key_obj = PyString_FromString(key); +#endif + if (key_obj == NULL) { + *result = NULL; + return -1; + } + res = PyDict_GetItemRef(mp, key_obj, result); + Py_DECREF(key_obj); + return res; +} +#endif + + +// gh-106307 added PyModule_Add() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyModule_Add(PyObject *mod, const char *name, PyObject *value) +{ + int res = PyModule_AddObjectRef(mod, name, value); + Py_XDECREF(value); + return res; +} +#endif + + +// gh-108014 added Py_IsFinalizing() to Python 3.13.0a1 +// bpo-1856 added _Py_Finalizing to Python 3.2.1b1. +// _Py_IsFinalizing() was added to PyPy 7.3.0. +#if (0x030201B1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030D00A1) \ + && (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000) +static inline int Py_IsFinalizing(void) +{ +#if PY_VERSION_HEX >= 0x030700A1 + // _Py_IsFinalizing() was added to Python 3.7.0a1. + return _Py_IsFinalizing(); +#else + return (_Py_Finalizing != NULL); +#endif +} +#endif + + +// gh-108323 added PyDict_ContainsString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyDict_ContainsString(PyObject *op, const char *key) +{ + PyObject *key_obj = PyUnicode_FromString(key); + if (key_obj == NULL) { + return -1; + } + int res = PyDict_Contains(op, key_obj); + Py_DECREF(key_obj); + return res; +} +#endif + + +// gh-108445 added PyLong_AsInt() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyLong_AsInt(PyObject *obj) +{ +#ifdef PYPY_VERSION + long value = PyLong_AsLong(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + if (value < (long)INT_MIN || (long)INT_MAX < value) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C int"); + return -1; + } + return (int)value; +#else + return _PyLong_AsInt(obj); +#endif +} +#endif + + +// gh-107073 added PyObject_VisitManagedDict() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) +{ + PyObject **dict = _PyObject_GetDictPtr(obj); + if (*dict == NULL) { + return -1; + } + Py_VISIT(*dict); + return 0; +} + +static inline void +PyObject_ClearManagedDict(PyObject *obj) +{ + PyObject **dict = _PyObject_GetDictPtr(obj); + if (*dict == NULL) { + return; + } + Py_CLEAR(*dict); +} +#endif + +// gh-108867 added PyThreadState_GetUnchecked() to Python 3.13.0a1 +// Python 3.5.2 added _PyThreadState_UncheckedGet(). +#if PY_VERSION_HEX >= 0x03050200 && PY_VERSION_HEX < 0x030D00A1 +static inline PyThreadState* +PyThreadState_GetUnchecked(void) +{ + return _PyThreadState_UncheckedGet(); +} +#endif + +// gh-110289 added PyUnicode_EqualToUTF8() and PyUnicode_EqualToUTF8AndSize() +// to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int +PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_len) +{ + Py_ssize_t len; + const void *utf8; + PyObject *exc_type, *exc_value, *exc_tb; + int res; + + // API cannot report errors so save/restore the exception + PyErr_Fetch(&exc_type, &exc_value, &exc_tb); + + // Python 3.3.0a1 added PyUnicode_AsUTF8AndSize() +#if PY_VERSION_HEX >= 0x030300A1 + if (PyUnicode_IS_ASCII(unicode)) { + utf8 = PyUnicode_DATA(unicode); + len = PyUnicode_GET_LENGTH(unicode); + } + else { + utf8 = PyUnicode_AsUTF8AndSize(unicode, &len); + if (utf8 == NULL) { + // Memory allocation failure. The API cannot report error, + // so ignore the exception and return 0. + res = 0; + goto done; + } + } + + if (len != str_len) { + res = 0; + goto done; + } + res = (memcmp(utf8, str, (size_t)len) == 0); +#else + PyObject *bytes = PyUnicode_AsUTF8String(unicode); + if (bytes == NULL) { + // Memory allocation failure. The API cannot report error, + // so ignore the exception and return 0. + res = 0; + goto done; + } + +#if PY_VERSION_HEX >= 0x03000000 + len = PyBytes_GET_SIZE(bytes); + utf8 = PyBytes_AS_STRING(bytes); +#else + len = PyString_GET_SIZE(bytes); + utf8 = PyString_AS_STRING(bytes); +#endif + if (len != str_len) { + Py_DECREF(bytes); + res = 0; + goto done; + } + + res = (memcmp(utf8, str, (size_t)len) == 0); + Py_DECREF(bytes); +#endif + +done: + PyErr_Restore(exc_type, exc_value, exc_tb); + return res; +} + +static inline int +PyUnicode_EqualToUTF8(PyObject *unicode, const char *str) +{ + return PyUnicode_EqualToUTF8AndSize(unicode, str, (Py_ssize_t)strlen(str)); +} +#endif + + +// gh-111138 added PyList_Extend() and PyList_Clear() to Python 3.13.0a2 +#if PY_VERSION_HEX < 0x030D00A2 +static inline int +PyList_Extend(PyObject *list, PyObject *iterable) +{ + return PyList_SetSlice(list, PY_SSIZE_T_MAX, PY_SSIZE_T_MAX, iterable); +} + +static inline int +PyList_Clear(PyObject *list) +{ + return PyList_SetSlice(list, 0, PY_SSIZE_T_MAX, NULL); +} +#endif + +// gh-111262 added PyDict_Pop() and PyDict_PopString() to Python 3.13.0a2 +#if PY_VERSION_HEX < 0x030D00A2 +static inline int +PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result) +{ + PyObject *value; + + if (!PyDict_Check(dict)) { + PyErr_BadInternalCall(); + if (result) { + *result = NULL; + } + return -1; + } + + // bpo-16991 added _PyDict_Pop() to Python 3.5.0b2. + // Python 3.6.0b3 changed _PyDict_Pop() first argument type to PyObject*. + // Python 3.13.0a1 removed _PyDict_Pop(). +#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x030500b2 || PY_VERSION_HEX >= 0x030D0000 + value = PyObject_CallMethod(dict, "pop", "O", key); +#elif PY_VERSION_HEX < 0x030600b3 + value = _PyDict_Pop(_Py_CAST(PyDictObject*, dict), key, NULL); +#else + value = _PyDict_Pop(dict, key, NULL); +#endif + if (value == NULL) { + if (result) { + *result = NULL; + } + if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_KeyError)) { + return -1; + } + PyErr_Clear(); + return 0; + } + if (result) { + *result = value; + } + else { + Py_DECREF(value); + } + return 1; +} + +static inline int +PyDict_PopString(PyObject *dict, const char *key, PyObject **result) +{ + PyObject *key_obj = PyUnicode_FromString(key); + if (key_obj == NULL) { + if (result != NULL) { + *result = NULL; + } + return -1; + } + + int res = PyDict_Pop(dict, key_obj, result); + Py_DECREF(key_obj); + return res; +} +#endif + + +#if PY_VERSION_HEX < 0x030200A4 +// Python 3.2.0a4 added Py_hash_t type +typedef Py_ssize_t Py_hash_t; +#endif + + +// gh-111545 added Py_HashPointer() to Python 3.13.0a3 +#if PY_VERSION_HEX < 0x030D00A3 +static inline Py_hash_t Py_HashPointer(const void *ptr) +{ +#if PY_VERSION_HEX >= 0x030900A4 && !defined(PYPY_VERSION) + return _Py_HashPointer(ptr); +#else + return _Py_HashPointer(_Py_CAST(void*, ptr)); +#endif +} +#endif + + +// Python 3.13a4 added a PyTime API. +// Use the private API added to Python 3.5. +#if PY_VERSION_HEX < 0x030D00A4 && PY_VERSION_HEX >= 0x03050000 +typedef _PyTime_t PyTime_t; +#define PyTime_MIN _PyTime_MIN +#define PyTime_MAX _PyTime_MAX + +static inline double PyTime_AsSecondsDouble(PyTime_t t) +{ return _PyTime_AsSecondsDouble(t); } + +static inline int PyTime_Monotonic(PyTime_t *result) +{ return _PyTime_GetMonotonicClockWithInfo(result, NULL); } + +static inline int PyTime_Time(PyTime_t *result) +{ return _PyTime_GetSystemClockWithInfo(result, NULL); } + +static inline int PyTime_PerfCounter(PyTime_t *result) +{ +#if PY_VERSION_HEX >= 0x03070000 && !defined(PYPY_VERSION) + return _PyTime_GetPerfCounterWithInfo(result, NULL); +#elif PY_VERSION_HEX >= 0x03070000 + // Call time.perf_counter_ns() and convert Python int object to PyTime_t. + // Cache time.perf_counter_ns() function for best performance. + static PyObject *func = NULL; + if (func == NULL) { + PyObject *mod = PyImport_ImportModule("time"); + if (mod == NULL) { + return -1; + } + + func = PyObject_GetAttrString(mod, "perf_counter_ns"); + Py_DECREF(mod); + if (func == NULL) { + return -1; + } + } + + PyObject *res = PyObject_CallNoArgs(func); + if (res == NULL) { + return -1; + } + long long value = PyLong_AsLongLong(res); + Py_DECREF(res); + + if (value == -1 && PyErr_Occurred()) { + return -1; + } + + Py_BUILD_ASSERT(sizeof(value) >= sizeof(PyTime_t)); + *result = (PyTime_t)value; + return 0; +#else + // Call time.perf_counter() and convert C double to PyTime_t. + // Cache time.perf_counter() function for best performance. + static PyObject *func = NULL; + if (func == NULL) { + PyObject *mod = PyImport_ImportModule("time"); + if (mod == NULL) { + return -1; + } + + func = PyObject_GetAttrString(mod, "perf_counter"); + Py_DECREF(mod); + if (func == NULL) { + return -1; + } + } + + PyObject *res = PyObject_CallNoArgs(func); + if (res == NULL) { + return -1; + } + double d = PyFloat_AsDouble(res); + Py_DECREF(res); + + if (d == -1.0 && PyErr_Occurred()) { + return -1; + } + + // Avoid floor() to avoid having to link to libm + *result = (PyTime_t)(d * 1e9); + return 0; +#endif +} + +#endif + +// gh-111389 added hash constants to Python 3.13.0a5. These constants were +// added first as private macros to Python 3.4.0b1 and PyPy 7.3.9. +#if (!defined(PyHASH_BITS) \ + && ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \ + || (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \ + && PYPY_VERSION_NUM >= 0x07090000))) +# define PyHASH_BITS _PyHASH_BITS +# define PyHASH_MODULUS _PyHASH_MODULUS +# define PyHASH_INF _PyHASH_INF +# define PyHASH_IMAG _PyHASH_IMAG +#endif + + +// gh-111545 added Py_GetConstant() and Py_GetConstantBorrowed() +// to Python 3.13.0a6 +#if PY_VERSION_HEX < 0x030D00A6 && !defined(Py_CONSTANT_NONE) + +#define Py_CONSTANT_NONE 0 +#define Py_CONSTANT_FALSE 1 +#define Py_CONSTANT_TRUE 2 +#define Py_CONSTANT_ELLIPSIS 3 +#define Py_CONSTANT_NOT_IMPLEMENTED 4 +#define Py_CONSTANT_ZERO 5 +#define Py_CONSTANT_ONE 6 +#define Py_CONSTANT_EMPTY_STR 7 +#define Py_CONSTANT_EMPTY_BYTES 8 +#define Py_CONSTANT_EMPTY_TUPLE 9 + +static inline PyObject* Py_GetConstant(unsigned int constant_id) +{ + static PyObject* constants[Py_CONSTANT_EMPTY_TUPLE + 1] = {NULL}; + + if (constants[Py_CONSTANT_NONE] == NULL) { + constants[Py_CONSTANT_NONE] = Py_None; + constants[Py_CONSTANT_FALSE] = Py_False; + constants[Py_CONSTANT_TRUE] = Py_True; + constants[Py_CONSTANT_ELLIPSIS] = Py_Ellipsis; + constants[Py_CONSTANT_NOT_IMPLEMENTED] = Py_NotImplemented; + + constants[Py_CONSTANT_ZERO] = PyLong_FromLong(0); + if (constants[Py_CONSTANT_ZERO] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_ONE] = PyLong_FromLong(1); + if (constants[Py_CONSTANT_ONE] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_STR] = PyUnicode_FromStringAndSize("", 0); + if (constants[Py_CONSTANT_EMPTY_STR] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_BYTES] = PyBytes_FromStringAndSize("", 0); + if (constants[Py_CONSTANT_EMPTY_BYTES] == NULL) { + goto fatal_error; + } + + constants[Py_CONSTANT_EMPTY_TUPLE] = PyTuple_New(0); + if (constants[Py_CONSTANT_EMPTY_TUPLE] == NULL) { + goto fatal_error; + } + // goto dance to avoid compiler warnings about Py_FatalError() + goto init_done; + +fatal_error: + // This case should never happen + Py_FatalError("Py_GetConstant() failed to get constants"); + } + +init_done: + if (constant_id <= Py_CONSTANT_EMPTY_TUPLE) { + return Py_NewRef(constants[constant_id]); + } + else { + PyErr_BadInternalCall(); + return NULL; + } +} + +static inline PyObject* Py_GetConstantBorrowed(unsigned int constant_id) +{ + PyObject *obj = Py_GetConstant(constant_id); + Py_XDECREF(obj); + return obj; +} +#endif + + +// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 +#if PY_VERSION_HEX < 0x030D00A4 +static inline PyObject * +PyList_GetItemRef(PyObject *op, Py_ssize_t index) +{ + PyObject *item = PyList_GetItem(op, index); + Py_XINCREF(item); + return item; +} +#endif + + +// gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 +#if PY_VERSION_HEX < 0x030D00A4 +static inline int +PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value, + PyObject **result) +{ + PyObject *value; + if (PyDict_GetItemRef(d, key, &value) < 0) { + // get error + if (result) { + *result = NULL; + } + return -1; + } + if (value != NULL) { + // present + if (result) { + *result = value; + } + else { + Py_DECREF(value); + } + return 1; + } + + // missing: set the item + if (PyDict_SetItem(d, key, default_value) < 0) { + // set error + if (result) { + *result = NULL; + } + return -1; + } + if (result) { + *result = Py_NewRef(default_value); + } + return 0; +} +#endif + +#if PY_VERSION_HEX < 0x030D00B3 +# define Py_BEGIN_CRITICAL_SECTION(op) { +# define Py_END_CRITICAL_SECTION() } +# define Py_BEGIN_CRITICAL_SECTION2(a, b) { +# define Py_END_CRITICAL_SECTION2() } +#endif + +#if PY_VERSION_HEX < 0x030E0000 && PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) +typedef struct PyUnicodeWriter PyUnicodeWriter; + +static inline void PyUnicodeWriter_Discard(PyUnicodeWriter *writer) +{ + _PyUnicodeWriter_Dealloc((_PyUnicodeWriter*)writer); + PyMem_Free(writer); +} + +static inline PyUnicodeWriter* PyUnicodeWriter_Create(Py_ssize_t length) +{ + if (length < 0) { + PyErr_SetString(PyExc_ValueError, + "length must be positive"); + return NULL; + } + + const size_t size = sizeof(_PyUnicodeWriter); + PyUnicodeWriter *pub_writer = (PyUnicodeWriter *)PyMem_Malloc(size); + if (pub_writer == _Py_NULL) { + PyErr_NoMemory(); + return _Py_NULL; + } + _PyUnicodeWriter *writer = (_PyUnicodeWriter *)pub_writer; + + _PyUnicodeWriter_Init(writer); + if (_PyUnicodeWriter_Prepare(writer, length, 127) < 0) { + PyUnicodeWriter_Discard(pub_writer); + return NULL; + } + writer->overallocate = 1; + return pub_writer; +} + +static inline PyObject* PyUnicodeWriter_Finish(PyUnicodeWriter *writer) +{ + PyObject *str = _PyUnicodeWriter_Finish((_PyUnicodeWriter*)writer); + assert(((_PyUnicodeWriter*)writer)->buffer == NULL); + PyMem_Free(writer); + return str; +} + +static inline int +PyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch) +{ + if (ch > 0x10ffff) { + PyErr_SetString(PyExc_ValueError, + "character must be in range(0x110000)"); + return -1; + } + + return _PyUnicodeWriter_WriteChar((_PyUnicodeWriter*)writer, ch); +} + +static inline int +PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, PyObject *obj) +{ + PyObject *str = PyObject_Str(obj); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} + +static inline int +PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj) +{ + PyObject *str = PyObject_Repr(obj); + if (str == NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} + +static inline int +PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, + const char *str, Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)strlen(str); + } + + PyObject *str_obj = PyUnicode_FromStringAndSize(str, size); + if (str_obj == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); + Py_DECREF(str_obj); + return res; +} + +static inline int +PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, + const wchar_t *str, Py_ssize_t size) +{ + if (size < 0) { + size = (Py_ssize_t)wcslen(str); + } + + PyObject *str_obj = PyUnicode_FromWideChar(str, size); + if (str_obj == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); + Py_DECREF(str_obj); + return res; +} + +static inline int +PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, + Py_ssize_t start, Py_ssize_t end) +{ + if (!PyUnicode_Check(str)) { + PyErr_Format(PyExc_TypeError, "expect str, not %T", str); + return -1; + } + if (start < 0 || start > end) { + PyErr_Format(PyExc_ValueError, "invalid start argument"); + return -1; + } + if (end > PyUnicode_GET_LENGTH(str)) { + PyErr_Format(PyExc_ValueError, "invalid end argument"); + return -1; + } + + return _PyUnicodeWriter_WriteSubstring((_PyUnicodeWriter*)writer, str, + start, end); +} + +static inline int +PyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...) +{ + va_list vargs; + va_start(vargs, format); + PyObject *str = PyUnicode_FromFormatV(format, vargs); + va_end(vargs); + if (str == _Py_NULL) { + return -1; + } + + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + Py_DECREF(str); + return res; +} +#endif // PY_VERSION_HEX < 0x030E0000 + +// gh-116560 added PyLong_GetSign() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyLong_GetSign(PyObject *obj, int *sign) +{ + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "expect int, got %s", Py_TYPE(obj)->tp_name); + return -1; + } + + *sign = _PyLong_Sign(obj); + return 0; +} +#endif + + +// gh-124502 added PyUnicode_Equal() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyUnicode_Equal(PyObject *str1, PyObject *str2) +{ + if (!PyUnicode_Check(str1)) { + PyErr_Format(PyExc_TypeError, "first argument must be str, not %s", + Py_TYPE(str1)->tp_name); + return -1; + } + if (!PyUnicode_Check(str2)) { + PyErr_Format(PyExc_TypeError, "second argument must be str, not %s", + Py_TYPE(str2)->tp_name); + return -1; + } + +#if PY_VERSION_HEX >= 0x030d0000 && !defined(PYPY_VERSION) + PyAPI_FUNC(int) _PyUnicode_Equal(PyObject *str1, PyObject *str2); + + return _PyUnicode_Equal(str1, str2); +#elif PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) + return _PyUnicode_EQ(str1, str2); +#elif PY_VERSION_HEX >= 0x03090000 && defined(PYPY_VERSION) + return _PyUnicode_EQ(str1, str2); +#else + return (PyUnicode_Compare(str1, str2) == 0); +#endif +} +#endif + + +// gh-121645 added PyBytes_Join() to Python 3.14.0a0 +#if PY_VERSION_HEX < 0x030E00A0 +static inline PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable) +{ + return _PyBytes_Join(sep, iterable); +} +#endif + + +#if PY_VERSION_HEX < 0x030E00A0 +static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) +{ +#if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) + PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void *src, Py_ssize_t len); + + return _Py_HashBytes(ptr, len); +#else + Py_hash_t hash; + PyObject *bytes = PyBytes_FromStringAndSize((const char*)ptr, len); + if (bytes == NULL) { + return -1; + } + hash = PyObject_Hash(bytes); + Py_DECREF(bytes); + return hash; +#endif +} +#endif + + +#if PY_VERSION_HEX < 0x030E00A0 +static inline int PyIter_NextItem(PyObject *iter, PyObject **item) +{ + iternextfunc tp_iternext; + + assert(iter != NULL); + assert(item != NULL); + + tp_iternext = Py_TYPE(iter)->tp_iternext; + if (tp_iternext == NULL) { + *item = NULL; + PyErr_Format(PyExc_TypeError, "expected an iterator, got '%s'", + Py_TYPE(iter)->tp_name); + return -1; + } + + if ((*item = tp_iternext(iter))) { + return 1; + } + if (!PyErr_Occurred()) { + return 0; + } + if (PyErr_ExceptionMatches(PyExc_StopIteration)) { + PyErr_Clear(); + return 0; + } + return -1; +} +#endif + + +#if PY_VERSION_HEX < 0x030E00A0 +static inline PyObject* PyLong_FromInt32(int32_t value) +{ + Py_BUILD_ASSERT(sizeof(long) >= 4); + return PyLong_FromLong(value); +} + +static inline PyObject* PyLong_FromInt64(int64_t value) +{ + Py_BUILD_ASSERT(sizeof(long long) >= 8); + return PyLong_FromLongLong(value); +} + +static inline PyObject* PyLong_FromUInt32(uint32_t value) +{ + Py_BUILD_ASSERT(sizeof(unsigned long) >= 4); + return PyLong_FromUnsignedLong(value); +} + +static inline PyObject* PyLong_FromUInt64(uint64_t value) +{ + Py_BUILD_ASSERT(sizeof(unsigned long long) >= 8); + return PyLong_FromUnsignedLongLong(value); +} + +static inline int PyLong_AsInt32(PyObject *obj, int32_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(int) == 4); + int value = PyLong_AsInt(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (int32_t)value; + return 0; +} + +static inline int PyLong_AsInt64(PyObject *obj, int64_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long long) == 8); + long long value = PyLong_AsLongLong(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (int64_t)value; + return 0; +} + +static inline int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long) >= 4); + unsigned long value = PyLong_AsUnsignedLong(obj); + if (value == (unsigned long)-1 && PyErr_Occurred()) { + return -1; + } +#if SIZEOF_LONG > 4 + if ((unsigned long)UINT32_MAX < value) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C uint32_t"); + return -1; + } +#endif + *pvalue = (uint32_t)value; + return 0; +} + +static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue) +{ + Py_BUILD_ASSERT(sizeof(long long) == 8); + unsigned long long value = PyLong_AsUnsignedLongLong(obj); + if (value == (unsigned long long)-1 && PyErr_Occurred()) { + return -1; + } + *pvalue = (uint64_t)value; + return 0; +} +#endif + + +#ifdef __cplusplus +} +#endif +#endif // PYTHONCAPI_COMPAT