|
| 1 | +/** |
| 2 | + * @file pyshim.hh |
| 3 | + * @author Tom Tang ([email protected]) |
| 4 | + * @brief Python's C APIs are constantly changing in different versions of CPython. |
| 5 | + * PythonMonkey has a wide variety of CPython versions' support. (Currently Python 3.8-3.13) |
| 6 | + * This file helps our Python API calls work with different Python versions in the same code base. |
| 7 | + * @date 2024-09-20 |
| 8 | + * |
| 9 | + * @copyright Copyright (c) 2024 Distributive Corp. |
| 10 | + * |
| 11 | + */ |
| 12 | + |
| 13 | +#ifndef PythonMonkey_py_version_shim_ |
| 14 | +#define PythonMonkey_py_version_shim_ |
| 15 | + |
| 16 | +#include <Python.h> |
| 17 | + |
| 18 | +/** |
| 19 | + * @brief `_Py_IsFinalizing` becomes a stable API in Python 3.13, |
| 20 | + * and renames to `Py_IsFinalizing` |
| 21 | + */ |
| 22 | +#if PY_VERSION_HEX >= 0x030d0000 // Python version is greater than 3.13 |
| 23 | + #define Py_IsFinalizing Py_IsFinalizing |
| 24 | +#else |
| 25 | + #define Py_IsFinalizing _Py_IsFinalizing |
| 26 | +#endif |
| 27 | + |
| 28 | +/** |
| 29 | + * @brief `_PyDictViewObject` type definition moved from Python's public API |
| 30 | + * to the **internal** header file `internal/pycore_dict.h` in Python 3.13. |
| 31 | + * |
| 32 | + * @see https://github.com/python/cpython/blob/v3.13.0rc1/Include/internal/pycore_dict.h#L64-L72 |
| 33 | + */ |
| 34 | +#if PY_VERSION_HEX >= 0x030d0000 // Python version is greater than 3.13 |
| 35 | +typedef struct { |
| 36 | + PyObject_HEAD |
| 37 | + PyDictObject *dv_dict; |
| 38 | +} _PyDictViewObject; |
| 39 | +#endif |
| 40 | + |
| 41 | +/** |
| 42 | + * @brief Shim for `_PyArg_CheckPositional`. |
| 43 | + * Since Python 3.13, `_PyArg_CheckPositional` function became an internal API. |
| 44 | + * @see Modified from https://github.com/python/cpython/blob/v3.13.0rc1/Python/getargs.c#L2738-L2780 |
| 45 | + */ |
| 46 | +#if PY_VERSION_HEX >= 0x030d0000 // Python version is greater than 3.13 |
| 47 | +inline int _PyArg_CheckPositional(const char *name, Py_ssize_t nargs, Py_ssize_t min, Py_ssize_t max) { |
| 48 | + if (!name) { // _PyArg_CheckPositional may also be when unpacking a tuple |
| 49 | + name = "unpacked tuple"; // https://github.com/python/cpython/blob/v3.13.0rc1/Python/getargs.c#L2746 |
| 50 | + } |
| 51 | + |
| 52 | + if (nargs < min) { |
| 53 | + PyErr_Format( |
| 54 | + PyExc_TypeError, |
| 55 | + "%.200s expected %s%zd argument%s, got %zd", |
| 56 | + name, (min == max ? "" : "at least "), min, min == 1 ? "" : "s", nargs); |
| 57 | + return 0; |
| 58 | + } |
| 59 | + |
| 60 | + if (nargs == 0) { |
| 61 | + return 1; |
| 62 | + } |
| 63 | + |
| 64 | + if (nargs > max) { |
| 65 | + PyErr_Format( |
| 66 | + PyExc_TypeError, |
| 67 | + "%.200s expected %s%zd argument%s, got %zd", |
| 68 | + name, (min == max ? "" : "at most "), max, max == 1 ? "" : "s", nargs); |
| 69 | + return 0; |
| 70 | + } |
| 71 | + |
| 72 | + return 1; |
| 73 | +} |
| 74 | +#endif |
| 75 | + |
| 76 | +/** |
| 77 | + * @brief Shim for `_PyDictView_New`. |
| 78 | + * Since Python 3.13, `_PyDictView_New` function became an internal API. |
| 79 | + * @see Modified from https://github.com/python/cpython/blob/v3.13.0rc1/Objects/dictobject.c#L5806-L5827 |
| 80 | + */ |
| 81 | +inline PyObject *PyDictView_New(PyObject *dict, PyTypeObject *type) { |
| 82 | +#if PY_VERSION_HEX < 0x030d0000 // Python version is lower than 3.13 |
| 83 | + return _PyDictView_New(dict, type); |
| 84 | +#else |
| 85 | + _PyDictViewObject *dv; |
| 86 | + dv = PyObject_GC_New(_PyDictViewObject, type); |
| 87 | + if (dv == NULL) |
| 88 | + return NULL; |
| 89 | + Py_INCREF(dict); |
| 90 | + dv->dv_dict = (PyDictObject *)dict; |
| 91 | + PyObject_GC_Track(dv); |
| 92 | + return (PyObject *)dv; |
| 93 | +#endif |
| 94 | +} |
| 95 | + |
| 96 | +/** |
| 97 | + * @brief Shim for `_PyErr_SetKeyError`. |
| 98 | + * Since Python 3.13, `_PyErr_SetKeyError` function became an internal API. |
| 99 | + */ |
| 100 | +inline void PyErr_SetKeyError(PyObject *key) { |
| 101 | + // Use the provided API when possible, as `PyErr_SetObject`'s behaviour is more complex than originally thought |
| 102 | + // see also: https://github.com/python/cpython/issues/101578 |
| 103 | +#if PY_VERSION_HEX < 0x030d0000 // Python version is lower than 3.13 |
| 104 | + return _PyErr_SetKeyError(key); |
| 105 | +#else |
| 106 | + return PyErr_SetObject(PyExc_KeyError, key); |
| 107 | +#endif |
| 108 | +} |
| 109 | + |
| 110 | +/** |
| 111 | + * @brief Shim for `Py_SET_SIZE`. |
| 112 | + * `Py_SET_SIZE` is not available in Python < 3.9 |
| 113 | + */ |
| 114 | +#ifndef Py_SET_SIZE |
| 115 | +static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) { |
| 116 | + ob->ob_size = size; |
| 117 | +} |
| 118 | +#define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject *)(ob), size) |
| 119 | +#endif |
| 120 | + |
| 121 | +/** |
| 122 | + * @brief Shim for `PyObject_CallOneArg`. |
| 123 | + * `PyObject_CallOneArg` is not available in Python < 3.9 |
| 124 | + */ |
| 125 | +#if PY_VERSION_HEX < 0x03090000 // Python version is less than 3.9 |
| 126 | +inline PyObject *PyObject_CallOneArg(PyObject *func, PyObject *arg) { |
| 127 | + return PyObject_CallFunction(func, "O", arg); |
| 128 | +} |
| 129 | +#endif |
| 130 | + |
| 131 | +/** |
| 132 | + * @brief Shim for `_PyLong_AsByteArray`. |
| 133 | + * Python 3.13.0a4 added a new public API `PyLong_AsNativeBytes()` to replace the private `_PyLong_AsByteArray()`. |
| 134 | + * But this change also modified the function signature of `_PyLong_AsByteArray()`. |
| 135 | + * @see https://github.com/python/cpython/issues/111140 |
| 136 | + */ |
| 137 | +inline int PyLong_AsByteArray(PyLongObject *v, unsigned char *bytes, size_t n, bool little_endian, bool is_signed) { |
| 138 | +#if PY_VERSION_HEX >= 0x030d0000 // Python version is 3.13 or higher |
| 139 | + return _PyLong_AsByteArray(v, bytes, n, little_endian, is_signed, /*with_exceptions*/ false); |
| 140 | +#else |
| 141 | + return _PyLong_AsByteArray(v, bytes, n, little_endian, is_signed); |
| 142 | +#endif |
| 143 | +} |
| 144 | + |
| 145 | +#endif // #ifndef PythonMonkey_py_version_shim_ |
0 commit comments