Skip to content

Commit a1f11b4

Browse files
authored
Merge branch 'Xmader/feat/python-3.13-support' into Xmader/fix/fix-mem-bugs-using-debug-build
2 parents 9474990 + 7ebe149 commit a1f11b4

File tree

7 files changed

+51
-33
lines changed

7 files changed

+51
-33
lines changed

include/pyshim.hh

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ inline int _PyArg_CheckPositional(const char *name, Py_ssize_t nargs, Py_ssize_t
7878
* Since Python 3.13, `_PyDictView_New` function became an internal API.
7979
* @see Modified from https://github.com/python/cpython/blob/v3.13.0rc1/Objects/dictobject.c#L5806-L5827
8080
*/
81-
inline PyObject *PyDictViewObject_new(PyObject *dict, PyTypeObject *type) {
81+
inline PyObject *PyDictView_New(PyObject *dict, PyTypeObject *type) {
8282
#if PY_VERSION_HEX < 0x030d0000 // Python version is lower than 3.13
8383
return _PyDictView_New(dict, type);
8484
#else
@@ -107,4 +107,39 @@ inline void PyErr_SetKeyError(PyObject *key) {
107107
#endif
108108
}
109109

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+
110145
#endif // #ifndef PythonMonkey_py_version_shim_

python/pythonmonkey/require.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,8 @@
8484
globalThis.python.stderr.read = lambda n: sys.stderr.read(n)
8585
# Python 3.13 dramatically changed how the namespace in `exec`/`eval` works
8686
# See https://docs.python.org/3.13/whatsnew/3.13.html#defined-mutation-semantics-for-locals
87-
_locals = {} # keep the local variables inside `eval`/`exec` to a dict
88-
globalThis.python.eval = lambda x: eval(str(x)[:], None, _locals)
89-
globalThis.python.exec = lambda x: exec(str(x)[:], None, _locals)
87+
globalThis.python.eval = lambda x: eval(str(x)[:], None, sys._getframe(1).f_locals)
88+
globalThis.python.exec = lambda x: exec(str(x)[:], None, sys._getframe(1).f_locals)
9089
globalThis.python.getenv = os.getenv
9190
globalThis.python.paths = sys.path
9291

src/ExceptionType.cc

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
#include <jsapi.h>
2020
#include <js/Exception.h>
2121

22+
#include <Python.h>
2223
#include <frameobject.h>
24+
#include "include/pyshim.hh"
2325

2426

2527
PyObject *ExceptionType::getPyObject(JSContext *cx, JS::HandleObject error) {
@@ -29,11 +31,7 @@ PyObject *ExceptionType::getPyObject(JSContext *cx, JS::HandleObject error) {
2931
PyObject *errStr = getExceptionString(cx, JS::ExceptionStack(cx, errValue, errStack), true);
3032

3133
// Construct a new SpiderMonkeyError python object
32-
#if PY_VERSION_HEX >= 0x03090000
3334
PyObject *pyObject = PyObject_CallOneArg(SpiderMonkeyError, errStr); // _PyErr_CreateException, https://github.com/python/cpython/blob/3.9/Python/errors.c#L100
34-
#else
35-
PyObject *pyObject = PyObject_CallFunction(SpiderMonkeyError, "O", errStr); // PyObject_CallOneArg is not available in Python < 3.9
36-
#endif
3735
Py_XDECREF(errStr);
3836

3937
// Preserve the original JS Error object as the Python Exception's `jsError` attribute for lossless two-way conversion

src/IntType.cc

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
#include <jsapi.h>
1515
#include <js/BigInt.h>
1616

17+
#include <Python.h>
18+
#include "include/pyshim.hh"
19+
1720
#include <vector>
1821

1922
#define SIGN_BIT_MASK 0b1000 // https://hg.mozilla.org/releases/mozilla-esr102/file/tip/js/src/vm/BigIntType.h#l40
@@ -44,11 +47,7 @@ static inline void PythonLong_SetSign(PyLongObject *op, int sign) {
4447
#else // Python version is less than 3.12
4548
// see https://github.com/python/cpython/blob/v3.9.16/Objects/longobject.c#L956
4649
Py_ssize_t pyDigitCount = Py_SIZE(op);
47-
#if PY_VERSION_HEX >= 0x03090000
4850
Py_SET_SIZE(op, sign * std::abs(pyDigitCount));
49-
#else
50-
((PyVarObject *)op)->ob_size = sign * std::abs(pyDigitCount); // Py_SET_SIZE is not available in Python < 3.9
51-
#endif
5251
#endif
5352
}
5453

@@ -102,11 +101,7 @@ PyObject *IntType::getPyObject(JSContext *cx, JS::BigInt *bigint) {
102101
// Cast to a pythonmonkey.bigint to differentiate it from a normal Python int,
103102
// allowing Py<->JS two-way BigInt conversion.
104103
// We don't do `Py_SET_TYPE` because `_PyLong_FromByteArray` may cache and reuse objects for small ints
105-
#if PY_VERSION_HEX >= 0x03090000
106104
PyObject *pyObject = PyObject_CallOneArg(getPythonMonkeyBigInt(), pyIntObj); // pyObject = pythonmonkey.bigint(pyIntObj)
107-
#else
108-
PyObject *pyObject = PyObject_CallFunction(getPythonMonkeyBigInt(), "O", pyIntObj); // PyObject_CallOneArg is not available in Python < 3.9
109-
#endif
110105
Py_DECREF(pyIntObj);
111106

112107
// Set the sign bit
@@ -139,11 +134,7 @@ JS::BigInt *IntType::toJsBigInt(JSContext *cx, PyObject *pyObject) {
139134
// Convert to bytes of 8-bit "digits" in **big-endian** order
140135
size_t byteCount = (size_t)JS_DIGIT_BYTE * jsDigitCount;
141136
uint8_t *bytes = (uint8_t *)PyMem_Malloc(byteCount);
142-
#if PY_VERSION_HEX >= 0x030d0000 // Python version is greater than 3.13
143-
_PyLong_AsByteArray((PyLongObject *)pyObject, bytes, byteCount, /*is_little_endian*/ false, false, false);
144-
#else
145-
_PyLong_AsByteArray((PyLongObject *)pyObject, bytes, byteCount, /*is_little_endian*/ false, false);
146-
#endif
137+
PyLong_AsByteArray((PyLongObject *)pyObject, bytes, byteCount, /*is_little_endian*/ false, false);
147138

148139
// Convert pm.bigint to JS::BigInt through hex strings (no public API to convert directly through bytes)
149140
// TODO (Tom Tang): We could manually allocate the memory, https://hg.mozilla.org/releases/mozilla-esr102/file/tip/js/src/vm/BigIntType.cpp#l162, but still no public API

src/JSObjectProxy.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -782,13 +782,13 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_update_method(JSObjectPr
782782
}
783783

784784
PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_keys_method(JSObjectProxy *self) {
785-
return PyDictViewObject_new((PyObject *)self, &JSObjectKeysProxyType);
785+
return PyDictView_New((PyObject *)self, &JSObjectKeysProxyType);
786786
}
787787

788788
PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_values_method(JSObjectProxy *self) {
789-
return PyDictViewObject_new((PyObject *)self, &JSObjectValuesProxyType);
789+
return PyDictView_New((PyObject *)self, &JSObjectValuesProxyType);
790790
}
791791

792792
PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_items_method(JSObjectProxy *self) {
793-
return PyDictViewObject_new((PyObject *)self, &JSObjectItemsProxyType);
793+
return PyDictView_New((PyObject *)self, &JSObjectItemsProxyType);
794794
}

src/PromiseType.cc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
#include <jsfriendapi.h>
2020
#include <js/Promise.h>
2121

22+
#include <Python.h>
23+
#include "include/pyshim.hh"
24+
2225
// slot ids to access the python object in JS callbacks
2326
#define PY_FUTURE_OBJ_SLOT 0
2427
#define PROMISE_OBJ_SLOT 1
@@ -38,11 +41,7 @@ static bool onResolvedCb(JSContext *cx, unsigned argc, JS::Value *vp) {
3841
if (state == JS::PromiseState::Rejected && !PyExceptionInstance_Check(result)) {
3942
// Wrap the result object into a SpiderMonkeyError object
4043
// because only *Exception objects can be thrown in Python `raise` statement and alike
41-
#if PY_VERSION_HEX >= 0x03090000
4244
PyObject *wrapped = PyObject_CallOneArg(SpiderMonkeyError, result); // wrapped = SpiderMonkeyError(result)
43-
#else
44-
PyObject *wrapped = PyObject_CallFunction(SpiderMonkeyError, "O", result); // PyObject_CallOneArg is not available in Python < 3.9
45-
#endif
4645
// Preserve the original JS value as the `jsError` attribute for lossless conversion back
4746
PyObject *originalJsErrCapsule = DictType::getPyObject(cx, resultArg);
4847
PyObject_SetAttrString(wrapped, "jsError", originalJsErrCapsule);

src/jsTypeFactory.cc

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -394,11 +394,7 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) {
394394

395395
// use faster calling if no arguments are needed
396396
if (((nNormalArgs + nDefaultArgs) <= 0 && !varargs)) {
397-
#if PY_VERSION_HEX >= 0x03090000
398-
pyRval = PyObject_CallNoArgs(pyFunc);
399-
#else
400-
pyRval = _PyObject_CallNoArg(pyFunc); // in Python 3.8, the API is only available under the name with a leading underscore
401-
#endif
397+
pyRval = PyObject_CallObject(pyFunc, NULL);
402398
if (PyErr_Occurred() && setPyException(cx)) { // Check if an exception has already been set in Python error stack
403399
goto failure;
404400
}

0 commit comments

Comments
 (0)