From 9ecfb2058c79c3c1692d7e489a91300333bea918 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Mon, 23 Jun 2025 10:21:43 -0400 Subject: [PATCH 1/2] Fix `_interpreters.set___main___attrs` SystemError when passing non-dict --- Lib/test/test__interpreters.py | 4 ++++ .../2025-06-23-10-19-11.gh-issue-135855.-J0AGF.rst | 3 +++ Modules/_interpretersmodule.c | 8 ++++---- 3 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-06-23-10-19-11.gh-issue-135855.-J0AGF.rst diff --git a/Lib/test/test__interpreters.py b/Lib/test/test__interpreters.py index ad3ebbfdff64a7..79e05f99b77c73 100644 --- a/Lib/test/test__interpreters.py +++ b/Lib/test/test__interpreters.py @@ -485,6 +485,10 @@ def test_signatures(self): msg = r'_interpreters.run_func\(\) argument 3 must be dict, not int' with self.assertRaisesRegex(TypeError, msg): _interpreters.run_func(self.id, lambda: None, shared=1) + # See https://github.com/python/cpython/issues/135855 + msg = r'_interpreters.set___main___attrs\(\) argument 2 must be dict, not int' + with self.assertRaisesRegex(TypeError, msg): + _interpreters.set___main___attrs(self.id, 1) def test_invalid_shared_encoding(self): # See https://github.com/python/cpython/issues/127196 diff --git a/Misc/NEWS.d/next/Library/2025-06-23-10-19-11.gh-issue-135855.-J0AGF.rst b/Misc/NEWS.d/next/Library/2025-06-23-10-19-11.gh-issue-135855.-J0AGF.rst new file mode 100644 index 00000000000000..fcf495bdceb168 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-23-10-19-11.gh-issue-135855.-J0AGF.rst @@ -0,0 +1,3 @@ +Raise :exc:`TypeError` instead of :exc:`SystemError` when +:func:`!_interpreters.set___main___attrs` is passed a non-dict object. +Patch by Brian Schubert. diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index e201ccd53daf8a..2fd88aee883b7a 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -1039,8 +1039,8 @@ interp_set___main___attrs(PyObject *self, PyObject *args, PyObject *kwargs) PyObject *id, *updates; int restricted = 0; if (!PyArg_ParseTupleAndKeywords(args, kwargs, - "OO|$p:" MODULE_NAME_STR ".set___main___attrs", - kwlist, &id, &updates, &restricted)) + "OO!|$p:" MODULE_NAME_STR ".set___main___attrs", + kwlist, &id, &PyDict_Type, &updates, &restricted)) { return NULL; } @@ -1055,13 +1055,13 @@ interp_set___main___attrs(PyObject *self, PyObject *args, PyObject *kwargs) // Check the updates. if (updates != Py_None) { - Py_ssize_t size = PyObject_Size(updates); + Py_ssize_t size = PyDict_Size(updates); if (size < 0) { return NULL; } if (size == 0) { PyErr_SetString(PyExc_ValueError, - "arg 2 must be a non-empty mapping"); + "arg 2 must be a non-empty dict"); return NULL; } } From f400fb8d33950623c874156d672d054703aa71e5 Mon Sep 17 00:00:00 2001 From: Brian Schubert Date: Tue, 24 Jun 2025 06:59:57 -0400 Subject: [PATCH 2/2] Remove redundant Py_None check, add None tests, add assert --- Lib/test/test__interpreters.py | 11 +++++++++++ Modules/_interpretersmodule.c | 18 ++++++++---------- Python/crossinterp.c | 1 + 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/Lib/test/test__interpreters.py b/Lib/test/test__interpreters.py index 79e05f99b77c73..a32d5d81d2bf2d 100644 --- a/Lib/test/test__interpreters.py +++ b/Lib/test/test__interpreters.py @@ -490,6 +490,17 @@ def test_signatures(self): with self.assertRaisesRegex(TypeError, msg): _interpreters.set___main___attrs(self.id, 1) + def test_invalid_shared_none(self): + msg = r'must be dict, not None' + with self.assertRaisesRegex(TypeError, msg): + _interpreters.exec(self.id, 'a', shared=None) + with self.assertRaisesRegex(TypeError, msg): + _interpreters.run_string(self.id, 'a', shared=None) + with self.assertRaisesRegex(TypeError, msg): + _interpreters.run_func(self.id, lambda: None, shared=None) + with self.assertRaisesRegex(TypeError, msg): + _interpreters.set___main___attrs(self.id, None) + def test_invalid_shared_encoding(self): # See https://github.com/python/cpython/issues/127196 bad_shared = {"\uD82A": 0} diff --git a/Modules/_interpretersmodule.c b/Modules/_interpretersmodule.c index 2fd88aee883b7a..b920c32474f4e6 100644 --- a/Modules/_interpretersmodule.c +++ b/Modules/_interpretersmodule.c @@ -1054,16 +1054,14 @@ interp_set___main___attrs(PyObject *self, PyObject *args, PyObject *kwargs) } // Check the updates. - if (updates != Py_None) { - Py_ssize_t size = PyDict_Size(updates); - if (size < 0) { - return NULL; - } - if (size == 0) { - PyErr_SetString(PyExc_ValueError, - "arg 2 must be a non-empty dict"); - return NULL; - } + Py_ssize_t size = PyDict_Size(updates); + if (size < 0) { + return NULL; + } + if (size == 0) { + PyErr_SetString(PyExc_ValueError, + "arg 2 must be a non-empty dict"); + return NULL; } _PyXI_session *session = _PyXI_NewSession(); diff --git a/Python/crossinterp.c b/Python/crossinterp.c index c44bcd559000b3..16a23f0351cd26 100644 --- a/Python/crossinterp.c +++ b/Python/crossinterp.c @@ -2617,6 +2617,7 @@ _PyXI_Enter(_PyXI_session *session, // Convert the attrs for cross-interpreter use. _PyXI_namespace *sharedns = NULL; if (nsupdates != NULL) { + assert(PyDict_Check(nsupdates)); Py_ssize_t len = PyDict_Size(nsupdates); if (len < 0) { if (result != NULL) {