diff --git a/source/blender/python/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt index 0daa887c937..ea38e94070b 100644 --- a/source/blender/python/generic/CMakeLists.txt +++ b/source/blender/python/generic/CMakeLists.txt @@ -20,6 +20,7 @@ set(SRC imbuf_py_api.cc py_capi_rna.cc py_capi_utils.cc + python_compat.cc bl_math_py_api.hh blf_py_api.hh @@ -28,9 +29,9 @@ set(SRC imbuf_py_api.hh py_capi_rna.hh py_capi_utils.hh + python_compat.hh # header-only - python_compat.hh python_utildefines.hh ) diff --git a/source/blender/python/generic/python_compat.cc b/source/blender/python/generic/python_compat.cc new file mode 100644 index 00000000000..c72a4c897c4 --- /dev/null +++ b/source/blender/python/generic/python_compat.cc @@ -0,0 +1,76 @@ + +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup pygen + * + * Functions relating to compatibility across Python versions. + */ + +#include /* IWYU pragma: keep. */ + +#include "BLI_utildefines.h" /* IWYU pragma: keep. */ +#include "python_compat.hh" /* IWYU pragma: keep. */ + +#if PY_VERSION_HEX >= 0x030e0000 /* >=3.14 */ + +/* Removed in Python 3.14. */ +int _PyArg_CheckPositional(const char *name, Py_ssize_t nargs, Py_ssize_t min, Py_ssize_t max) +{ + BLI_assert(min >= 0); + BLI_assert(min <= max); + + if (nargs < min) { + if (name != nullptr) { + PyErr_Format(PyExc_TypeError, + "%.200s expected %s%zd argument%s, got %zd", + name, + (min == max ? "" : "at least "), + min, + min == 1 ? "" : "s", + nargs); + } + else { + PyErr_Format(PyExc_TypeError, + "unpacked tuple should have %s%zd element%s," + " but has %zd", + (min == max ? "" : "at least "), + min, + min == 1 ? "" : "s", + nargs); + } + return 0; + } + + if (nargs == 0) { + return 1; + } + + if (nargs > max) { + if (name != nullptr) { + PyErr_Format(PyExc_TypeError, + "%.200s expected %s%zd argument%s, got %zd", + name, + (min == max ? "" : "at most "), + max, + max == 1 ? "" : "s", + nargs); + } + else { + PyErr_Format(PyExc_TypeError, + "unpacked tuple should have %s%zd element%s," + " but has %zd", + (min == max ? "" : "at most "), + max, + max == 1 ? "" : "s", + nargs); + } + return 0; + } + + return 1; +} + +#endif diff --git a/source/blender/python/generic/python_compat.hh b/source/blender/python/generic/python_compat.hh index 58f36280423..a44faf99b73 100644 --- a/source/blender/python/generic/python_compat.hh +++ b/source/blender/python/generic/python_compat.hh @@ -48,6 +48,7 @@ #if PY_VERSION_HEX < 0x030e0000 # define Py_HashPointer _Py_HashPointer # define PyThreadState_GetUnchecked _PyThreadState_UncheckedGet - /* TODO: Support: `PyDict_Pop`, it has different arguments. */ +#else /* >= 3.14 */ +int _PyArg_CheckPositional(const char *name, Py_ssize_t nargs, Py_ssize_t min, Py_ssize_t max); #endif diff --git a/source/blender/python/mathutils/mathutils_geometry.cc b/source/blender/python/mathutils/mathutils_geometry.cc index 4d31c2005e9..a7d22caa04b 100644 --- a/source/blender/python/mathutils/mathutils_geometry.cc +++ b/source/blender/python/mathutils/mathutils_geometry.cc @@ -29,6 +29,7 @@ #include "BLI_utildefines.h" #include "../generic/py_capi_utils.hh" +#include "../generic/python_compat.hh" /* IWYU pragma: keep. */ #include "../generic/python_utildefines.hh" /* ---------------------------------INTERSECTION FUNCTIONS-------------------- */