Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python 3.13 support #442

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open

python 3.13 support #442

wants to merge 36 commits into from

Conversation

Xmader
Copy link
Member

@Xmader Xmader commented Sep 12, 2024

In order to make PythonMonkey support the upcoming (October 1, 2024) Python 3.13 release, the following changes/actions need to be made in the codebase:


Closes #441

`_Py_IsFinalizing` becomes a stable API in Python 3.13, and is renamed to `Py_IsFinalizing`
https://docs.python.org/3.13/c-api/init.html#c.Py_IsFinalizing
Since Python 3.13, `_PyDictView_New` function became an internal API.
Python 3.13 moved this undocumented function to private API.

See python/cpython#108607
@Xmader Xmader marked this pull request as ready for review September 20, 2024 08:43
@Xmader Xmader force-pushed the Xmader/feat/python-3.13-support branch from 56aed76 to 46909e3 Compare September 23, 2024 21:58
@Xmader Xmader force-pushed the Xmader/feat/python-3.13-support branch from 46909e3 to 623d83e Compare September 23, 2024 22:13
`_PyErr_SetKeyError` is more complex than originally thought, use the provided API when possible

See also python/cpython#101578
Copy link
Collaborator

@caleb-distributive caleb-distributive left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that only on python 3.13, the eval-test.simple test says

"Exception ignored in: <_io.BufferedReader name='/home/runner/work/PythonMonkey/PythonMonkey/tests/js/resources/eval-test.js'>"

I looked into it briefly, looks to be something to do with the file being closed more than once somehow.

Copy link
Collaborator

@caleb-distributive caleb-distributive left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks great, just some minor changes

Comment on lines +85 to 90
# Python 3.13 dramatically changed how the namespace in `exec`/`eval` works
# See https://docs.python.org/3.13/whatsnew/3.13.html#defined-mutation-semantics-for-locals
_locals = {} # keep the local variables inside `eval`/`exec` to a dict
globalThis.python.eval = lambda x: eval(x, None, _locals)
globalThis.python.exec = lambda x: exec(x, None, _locals)
globalThis.python.getenv = os.getenv
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Python 3.13 dramatically changed how the namespace in `exec`/`eval` works
# See https://docs.python.org/3.13/whatsnew/3.13.html#defined-mutation-semantics-for-locals
_locals = {} # keep the local variables inside `eval`/`exec` to a dict
globalThis.python.eval = lambda x: eval(x, None, _locals)
globalThis.python.exec = lambda x: exec(x, None, _locals)
globalThis.python.getenv = os.getenv
# Python 3.13 dramatically changed how the namespace in `exec`/`eval` works
# See https://docs.python.org/3.13/whatsnew/3.13.html#defined-mutation-semantics-for-locals
globalThis.python.eval = lambda x: eval(x, None, sys._getframe(1).f_locals)
globalThis.python.exec = lambda x: exec(x, None, sys._getframe(1).f_locals)

Without the above change, pythonmonkey would lose the ability to mutate parent scope variables with pm.eval. On main the following assert passes, but it currently fails on this branch. We should add a test to ensure this behaviour as well.

import pythonmonkey as pm

a = 42

pm.eval("python.exec('a = 43')")
assert a == 43

While on the subject, the following currently fails on main, but probably shouldn't. The above change fixes that as well.

import pythonmonkey as pm
def foo():
  b = 42
  pm.eval("python.exec('b = 43')")
  assert b == 43
foo()

Comment on lines +142 to +146
#if PY_VERSION_HEX >= 0x030d0000 // Python version is greater than 3.13
_PyLong_AsByteArray((PyLongObject *)pyObject, bytes, byteCount, /*is_little_endian*/ false, false, false);
#else
_PyLong_AsByteArray((PyLongObject *)pyObject, bytes, byteCount, /*is_little_endian*/ false, false);
#endif
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#if PY_VERSION_HEX >= 0x030d0000 // Python version is greater than 3.13
_PyLong_AsByteArray((PyLongObject *)pyObject, bytes, byteCount, /*is_little_endian*/ false, false, false);
#else
_PyLong_AsByteArray((PyLongObject *)pyObject, bytes, byteCount, /*is_little_endian*/ false, false);
#endif
PyLong_AsByteArray((PyLongObject *)pyObject, bytes, byteCount, /*is_little_endian*/ false, false, false);

Since we're making a shim now, it might be best to include a shim for this as well, like:

/**
 * @brief Shim for `_PyLong_AsByteArray`.
 *        Since Python 3.13, `_PyLong_AsByteArray`'s signature changed.
 * @see https://github.com/python/cpython/issues/111140
 */
inline int PyLong_AsByteArray(PyLongObject *v,
  unsigned char *bytes, size_t n,
  int little_endian, int is_signed, int with_exceptions) {
#if PY_VERSION_HEX >= 0x030d0000 // Python version is 3.13 or higher
  return _PyLong_AsByteArray(v, bytes, n, little_endian, is_signed, with_exceptions);
#else
  return _PyLong_AsByteArray(v, bytes, n, little_endian, is_signed);
#endif
}

* Since Python 3.13, `_PyDictView_New` function became an internal API.
* @see Modified from https://github.com/python/cpython/blob/v3.13.0rc1/Objects/dictobject.c#L5806-L5827
*/
inline PyObject *PyDictViewObject_new(PyObject *dict, PyTypeObject *type) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
inline PyObject *PyDictViewObject_new(PyObject *dict, PyTypeObject *type) {
inline PyObject *PyDictView_New(PyObject *dict, PyTypeObject *type) {

I think this would be a better name for this shim

Comment on lines +171 to +173
ts_dict = _PyThreadState_GetDict(tstate); // borrowed reference
#else // Python 3.8
PyObject *ts_dict = tstate->dict; // see https://github.com/python/cpython/blob/v3.8.17/Modules/_asynciomodule.c#L244-L245
ts_dict = tstate->dict; // see https://github.com/python/cpython/blob/v3.8.17/Modules/_asynciomodule.c#L244-L245
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could potentially make a shim for _PyThreadState_GetDict here for 3.8

#endif
}

#endif // #ifndef PythonMonkey_py_version_shim_
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#endif // #ifndef PythonMonkey_py_version_shim_
/**
* @brief Shim for `PyObject_CallOneArg`.
* `PyObject_CallOneArg` does not exist in Python 3.8.
*/
#if PY_VERSION_HEX < 0x03090000 // Python version is less than 3.9
inline PyObject *PyObject_CallOneArg(PyObject *func, PyObject *arg) {
return PyObject_CallFunction(func, "O", arg);
}
#endif
#endif // #ifndef PythonMonkey_py_version_shim_

This shim can be used to simplify code at ExceptionType.cc:32, IntType.cc:106, and PromiseType.cc:41

#endif
}

#endif // #ifndef PythonMonkey_py_version_shim_
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#endif // #ifndef PythonMonkey_py_version_shim_
/**
* @brief Shim for `Py_SET_SIZE`.
* `Py_SET_SIZE` does not exist in Python 3.8.
*/
#if PY_VERSION_HEX < 0x03090000 // Python version is less than 3.9
inline void Py_SET_SIZE(PyVarObject *o, Py_ssize_t size) {
o->ob_size = size;
}
#endif
#endif // #ifndef PythonMonkey_py_version_shim_

This shim can be used to simplify code at IntType.cc:48

#endif
}

#endif // #ifndef PythonMonkey_py_version_shim_
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#endif // #ifndef PythonMonkey_py_version_shim_
#if PY_VERSION_HEX < 0x03090000 // Python version is less than 3.9
inline PyObject *PyObject_CallNoArgs(PyObject *func) {
return _PyObject_CallNoArg(func);
}
#endif
#endif // #ifndef PythonMonkey_py_version_shim_

This shim can be used to simplify code at jsTypeFactory.cc:398

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Python 3.13 support
3 participants