From 970e3d924ea198e958bd323050af030b081cc911 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 09:52:06 +0800 Subject: [PATCH 01/49] allow a mapping, rather than just a dict subclass, as globals --- Lib/test/test_capi/test_eval_code_ex.py | 6 +++--- Lib/test/test_capi/test_run.py | 12 ++++++------ Lib/test/test_compile.py | 2 +- Objects/frameobject.c | 12 ++++++------ Objects/funcobject.c | 4 ++-- Python/bltinmodule.c | 18 ++++++++---------- Python/bytecodes.c | 6 +++--- Python/generated_cases.c.h | 2 +- Python/pythonrun.c | 8 ++++---- 9 files changed, 34 insertions(+), 36 deletions(-) diff --git a/Lib/test/test_capi/test_eval_code_ex.py b/Lib/test/test_capi/test_eval_code_ex.py index b298e5007e5e7d..181f63734e2ae8 100644 --- a/Lib/test/test_capi/test_eval_code_ex.py +++ b/Lib/test/test_capi/test_eval_code_ex.py @@ -23,9 +23,9 @@ def f(): self.assertEqual(eval_code_ex(code, dict(a=1)), 1) self.assertRaises(NameError, eval_code_ex, code, {}) - self.assertRaises(SystemError, eval_code_ex, code, UserDict(a=1)) - self.assertRaises(SystemError, eval_code_ex, code, []) - self.assertRaises(SystemError, eval_code_ex, code, 1) + eval_code_ex(code, UserDict(a=1)) + self.assertRaises(TypeError, eval_code_ex, code, []) + self.assertRaises(TypeError, eval_code_ex, code, 1) # CRASHES eval_code_ex(code, NULL) # CRASHES eval_code_ex(1, {}) # CRASHES eval_code_ex(NULL, {}) diff --git a/Lib/test/test_capi/test_run.py b/Lib/test/test_capi/test_run.py index 894f66b437a39c..ddaa7d981389c3 100644 --- a/Lib/test/test_capi/test_run.py +++ b/Lib/test/test_capi/test_run.py @@ -64,9 +64,9 @@ def run(s, *args): self.assertRaises(SystemError, run, b'a\n', NULL) self.assertRaises(SystemError, run, b'a\n', NULL, {}) self.assertRaises(SystemError, run, b'a\n', NULL, dict(a=1)) - self.assertRaises(SystemError, run, b'a\n', UserDict()) - self.assertRaises(SystemError, run, b'a\n', UserDict(), {}) - self.assertRaises(SystemError, run, b'a\n', UserDict(), dict(a=1)) + self.assertRaises(NameError, run, b'a\n', UserDict()) + self.assertRaises(NameError, run, b'a\n', UserDict(), {}) + run(b'a\n', UserDict(), dict(a=1)) # CRASHES run(NULL, {}) @@ -97,9 +97,9 @@ def run(*args): self.assertRaises(SystemError, run, NULL) self.assertRaises(SystemError, run, NULL, {}) self.assertRaises(SystemError, run, NULL, dict(a=1)) - self.assertRaises(SystemError, run, UserDict()) - self.assertRaises(SystemError, run, UserDict(), {}) - self.assertRaises(SystemError, run, UserDict(), dict(a=1)) + self.assertRaises(NameError, run, UserDict()) + self.assertRaises(NameError, run, UserDict(), {}) + run(UserDict(), dict(a=1)) @unittest.skipUnless(TESTFN_UNDECODABLE, 'only works if there are undecodable paths') @unittest.skipIf(os.name == 'nt', 'does not work on Windows') diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 9def47e101b496..361b518d94cea6 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -97,7 +97,7 @@ def keys(self): self.assertEqual(m.results, ('z', g)) exec('z = locals()', g, m) self.assertEqual(m.results, ('z', m)) - self.assertRaises(TypeError, exec, 'z = b', m) + self.assertRaises(NameError, exec, 'z = b', m) class A: "Non-mapping" diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 3dc9ff058a5c9e..360ed64d734483 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -2105,17 +2105,17 @@ PyFrame_GetGenerator(PyFrameObject *frame) PyObject* _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) { - PyObject *builtins = PyDict_GetItemWithError(globals, &_Py_ID(__builtins__)); - if (builtins) { + PyObject *builtins; + int has_builtins = PyMapping_GetOptionalItem(globals, &_Py_ID(__builtins__), &builtins); + if (has_builtins < 0) { + return NULL; + } + if (has_builtins) { if (PyModule_Check(builtins)) { builtins = _PyModule_GetDict(builtins); assert(builtins != NULL); } return builtins; } - if (PyErr_Occurred()) { - return NULL; - } - return _PyEval_GetBuiltins(tstate); } diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 40211297be20c0..653a7533d33877 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -103,7 +103,7 @@ PyFunctionObject * _PyFunction_FromConstructor(PyFrameConstructor *constr) { PyObject *module; - if (PyDict_GetItemRef(constr->fc_globals, &_Py_ID(__name__), &module) < 0) { + if (PyMapping_GetOptionalItemString(constr->fc_globals, "__name__", &module) < 0) { return NULL; } @@ -174,7 +174,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname // __module__: Use globals['__name__'] if it exists, or NULL. PyObject *module; PyObject *builtins = NULL; - if (PyDict_GetItemRef(globals, &_Py_ID(__name__), &module) < 0) { + if (PyMapping_GetOptionalItemString(globals, "__name__", &module) < 0) { goto error; } diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 6e50623cafa4ed..628451691588ca 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -963,10 +963,8 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, PyErr_SetString(PyExc_TypeError, "locals must be a mapping"); return NULL; } - if (globals != Py_None && !PyDict_Check(globals)) { - PyErr_SetString(PyExc_TypeError, PyMapping_Check(globals) ? - "globals must be a real dict; try eval(expr, {}, mapping)" - : "globals must be a dict"); + if (globals != Py_None && !PyMapping_Check(globals)) { + PyErr_SetString(PyExc_TypeError, "globals must be a mapping"); return NULL; } if (globals == Py_None) { @@ -993,9 +991,9 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, goto error; } - int r = PyDict_Contains(globals, &_Py_ID(__builtins__)); + int r = PyMapping_HasKeyStringWithError(globals, "__builtins__"); if (r == 0) { - r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); + r = PyMapping_SetItemString(globals, "__builtins__", PyEval_GetBuiltins()); } if (r < 0) { goto error; @@ -1084,8 +1082,8 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, Py_INCREF(locals); } - if (!PyDict_Check(globals)) { - PyErr_Format(PyExc_TypeError, "exec() globals must be a dict, not %.100s", + if (!PyMapping_Check(globals)) { + PyErr_Format(PyExc_TypeError, "globals must be a mapping or None, not %.100s", Py_TYPE(globals)->tp_name); goto error; } @@ -1095,9 +1093,9 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, Py_TYPE(locals)->tp_name); goto error; } - int r = PyDict_Contains(globals, &_Py_ID(__builtins__)); + int r = PyMapping_HasKeyString(globals, "__builtins__"); if (r == 0) { - r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); + r = PyMapping_SetItemString(globals, "__builtins__", PyEval_GetBuiltins()); } if (r < 0) { goto error; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 76587a4f0dc695..de308416b9d973 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1466,14 +1466,14 @@ dummy_func( inst(STORE_GLOBAL, (v --)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyDict_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); + int err = PyObject_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); DECREF_INPUTS(); ERROR_IF(err, error); } inst(DELETE_GLOBAL, (--)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyDict_Pop(GLOBALS(), name, NULL); + int err = PyMapping_DelItem(GLOBALS(), name); // Can't use ERROR_IF here. if (err < 0) { ERROR_NO_POP(); @@ -1551,7 +1551,7 @@ dummy_func( ERROR_NO_POP(); } if (v_o == NULL) { - if (PyDict_GetItemRef(GLOBALS(), name, &v_o) < 0) { + if (PyMapping_GetItemRef(GLOBALS(), name, &v_o) < 0) { ERROR_NO_POP(); } if (v_o == NULL) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 61057221291c0a..8c66984a7b008c 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -4985,7 +4985,7 @@ goto error; } if (v_o == NULL) { - if (PyDict_GetItemRef(GLOBALS(), name, &v_o) < 0) { + if (PyMapping_GetOptionalItem(GLOBALS(), name, &v_o) < 0) { goto error; } if (v_o == NULL) { diff --git a/Python/pythonrun.c b/Python/pythonrun.c index ce7f194e929c9c..cd47ef3757eb20 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1273,16 +1273,16 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py _PyRuntime.signals.unhandled_keyboard_interrupt = 0; /* Set globals['__builtins__'] if it doesn't exist */ - if (!globals || !PyDict_Check(globals)) { - PyErr_SetString(PyExc_SystemError, "globals must be a real dict"); + if (!globals || !PyMapping_Check(globals)) { + PyErr_SetString(PyExc_SystemError, "globals must be a mapping"); return NULL; } - int has_builtins = PyDict_ContainsString(globals, "__builtins__"); + int has_builtins = PyMapping_HasKeyStringWithError(globals, "__builtins__"); if (has_builtins < 0) { return NULL; } if (!has_builtins) { - if (PyDict_SetItemString(globals, "__builtins__", + if (PyMapping_SetItemString(globals, "__builtins__", tstate->interp->builtins) < 0) { return NULL; From cf9945e9266e48b53c85d18303f70e29a76b9030 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 10:14:39 +0800 Subject: [PATCH 02/49] fixed typo --- Python/bytecodes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index de308416b9d973..8568d4656b8dba 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1551,7 +1551,7 @@ dummy_func( ERROR_NO_POP(); } if (v_o == NULL) { - if (PyMapping_GetItemRef(GLOBALS(), name, &v_o) < 0) { + if (PyMapping_GetOptionalItem(GLOBALS(), name, &v_o) < 0) { ERROR_NO_POP(); } if (v_o == NULL) { From 55f6dd4537dadb924ec8e1d784419e7c0410a6ca Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 10:19:14 +0800 Subject: [PATCH 03/49] regenerated header files --- Python/executor_cases.c.h | 4 ++-- Python/generated_cases.c.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 3b999465aac815..a70bfbf5ae0a80 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1510,7 +1510,7 @@ oparg = CURRENT_OPARG(); v = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyDict_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); + int err = PyObject_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); PyStackRef_CLOSE(v); if (err) JUMP_TO_ERROR(); stack_pointer += -1; @@ -1521,7 +1521,7 @@ case _DELETE_GLOBAL: { oparg = CURRENT_OPARG(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyDict_Pop(GLOBALS(), name, NULL); + int err = PyMapping_DelItem(GLOBALS(), name); // Can't use ERROR_IF here. if (err < 0) { JUMP_TO_ERROR(); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 8c66984a7b008c..ab2078eda434ac 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2778,7 +2778,7 @@ next_instr += 1; INSTRUCTION_STATS(DELETE_GLOBAL); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyDict_Pop(GLOBALS(), name, NULL); + int err = PyMapping_DelItem(GLOBALS(), name); // Can't use ERROR_IF here. if (err < 0) { goto error; @@ -6183,7 +6183,7 @@ _PyStackRef v; v = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyDict_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); + int err = PyObject_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); PyStackRef_CLOSE(v); if (err) goto pop_1_error; stack_pointer += -1; From 0d347f86476821680b12f55fbf61721e6cb41bbd Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 13:00:56 +0800 Subject: [PATCH 04/49] fixed error handling of DELETE_GLOBAL --- Python/bytecodes.c | 9 ++++----- Python/executor_cases.c.h | 9 ++++----- Python/generated_cases.c.h | 9 ++++----- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 8568d4656b8dba..dd06d8c869633e 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1476,11 +1476,10 @@ dummy_func( int err = PyMapping_DelItem(GLOBALS(), name); // Can't use ERROR_IF here. if (err < 0) { - ERROR_NO_POP(); - } - if (err == 0) { - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + } ERROR_NO_POP(); } } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index a70bfbf5ae0a80..a1b9accb8e7e7d 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1524,11 +1524,10 @@ int err = PyMapping_DelItem(GLOBALS(), name); // Can't use ERROR_IF here. if (err < 0) { - JUMP_TO_ERROR(); - } - if (err == 0) { - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + } JUMP_TO_ERROR(); } break; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ab2078eda434ac..0291d81298b044 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2781,11 +2781,10 @@ int err = PyMapping_DelItem(GLOBALS(), name); // Can't use ERROR_IF here. if (err < 0) { - goto error; - } - if (err == 0) { - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + } goto error; } DISPATCH(); From 4e1af59d1747818c547df81fd93a71e0d3079a2c Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 13:20:53 +0800 Subject: [PATCH 05/49] fixed borrowed builtins reference --- Objects/frameobject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 360ed64d734483..a66ca175d2cc2a 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -2111,6 +2111,7 @@ _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) return NULL; } if (has_builtins) { + Py_DECREF(builtins); if (PyModule_Check(builtins)) { builtins = _PyModule_GetDict(builtins); assert(builtins != NULL); From 40f7c35494654961a127cb61fefd0d3ebfc029ca Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 13:37:08 +0800 Subject: [PATCH 06/49] fixed error handling --- Objects/frameobject.c | 4 +++- Python/bytecodes.c | 3 ++- Python/executor_cases.c.h | 3 ++- Python/generated_cases.c.h | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index a66ca175d2cc2a..8004f9fc5edec7 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -2106,11 +2106,13 @@ PyObject* _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) { PyObject *builtins; - int has_builtins = PyMapping_GetOptionalItem(globals, &_Py_ID(__builtins__), &builtins); + int has_builtins = PyMapping_GetOptionalItem( + globals, &_Py_ID(__builtins__), &builtins); if (has_builtins < 0) { return NULL; } if (has_builtins) { + /* release reference right away since we mean to only borrow it */ Py_DECREF(builtins); if (PyModule_Check(builtins)) { builtins = _PyModule_GetDict(builtins); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index dd06d8c869633e..61850aff1ab722 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1476,7 +1476,8 @@ dummy_func( int err = PyMapping_DelItem(GLOBALS(), name); // Can't use ERROR_IF here. if (err < 0) { - if (PyErr_ExceptionMatches(PyExc_KeyError)) { + if (_PyErr_Occurred(tstate) && + _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, NAME_ERROR_MSG, name); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index a1b9accb8e7e7d..6cf910afc52ad5 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1524,7 +1524,8 @@ int err = PyMapping_DelItem(GLOBALS(), name); // Can't use ERROR_IF here. if (err < 0) { - if (PyErr_ExceptionMatches(PyExc_KeyError)) { + if (_PyErr_Occurred(tstate) && + _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, NAME_ERROR_MSG, name); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 0291d81298b044..760d759402e1bb 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2781,7 +2781,8 @@ int err = PyMapping_DelItem(GLOBALS(), name); // Can't use ERROR_IF here. if (err < 0) { - if (PyErr_ExceptionMatches(PyExc_KeyError)) { + if (_PyErr_Occurred(tstate) && + _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, NAME_ERROR_MSG, name); } From 698aa1f639b46a4ffea72e038ccb8d2c3ff32ef8 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 14:40:18 +0800 Subject: [PATCH 07/49] use PyMapping_HasKeyStringWithError for proper error handling --- Python/bltinmodule.c | 2 +- Python/ceval.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 628451691588ca..af5fbaabcf6980 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1093,7 +1093,7 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, Py_TYPE(locals)->tp_name); goto error; } - int r = PyMapping_HasKeyString(globals, "__builtins__"); + int r = PyMapping_HasKeyStringWithError(globals, "__builtins__"); if (r == 0) { r = PyMapping_SetItemString(globals, "__builtins__", PyEval_GetBuiltins()); } diff --git a/Python/ceval.c b/Python/ceval.c index a240ed4321f7ee..3853c9bdb51386 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -205,7 +205,7 @@ maybe_lltrace_resume_frame(_PyInterpreterFrame *frame, _PyInterpreterFrame *skip if (frame == skip_frame) { return 0; } - int r = PyDict_Contains(globals, &_Py_ID(__lltrace__)); + int r = PyMapping_HasKeyStringWithError(globals, "__lltrace__"); if (r < 0) { return -1; } From fa1dcac867eec6f1726c28eb518f9a94163520ed Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 14:49:15 +0800 Subject: [PATCH 08/49] fixed usage of id __lltrace__ --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/ceval.c b/Python/ceval.c index 3853c9bdb51386..959fd7ead37b73 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -205,7 +205,7 @@ maybe_lltrace_resume_frame(_PyInterpreterFrame *frame, _PyInterpreterFrame *skip if (frame == skip_frame) { return 0; } - int r = PyMapping_HasKeyStringWithError(globals, "__lltrace__"); + int r = PyMapping_HasKeyWithError(globals, &_Py_ID(__lltrace__)); if (r < 0) { return -1; } From 434386702cfb29233d4e421e827e1eb2217c5209 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 15:16:05 +0800 Subject: [PATCH 09/49] made _PyEval_BuiltinsFromGlobals return a new, non-borrowed reference --- Objects/frameobject.c | 6 ++---- Objects/funcobject.c | 3 +-- Python/ceval.c | 2 ++ 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 8004f9fc5edec7..c07cb00630fbcb 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -2112,13 +2112,11 @@ _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) return NULL; } if (has_builtins) { - /* release reference right away since we mean to only borrow it */ - Py_DECREF(builtins); if (PyModule_Check(builtins)) { - builtins = _PyModule_GetDict(builtins); + builtins = Py_XNewRef(_PyModule_GetDict(builtins)); assert(builtins != NULL); } return builtins; } - return _PyEval_GetBuiltins(tstate); + return Py_NewRef(_PyEval_GetBuiltins(tstate)); } diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 653a7533d33877..7dfff4ae9cfe31 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -178,11 +178,10 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname goto error; } - builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); // borrowed ref + builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); if (builtins == NULL) { goto error; } - Py_INCREF(builtins); PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); if (op == NULL) { diff --git a/Python/ceval.c b/Python/ceval.c index 959fd7ead37b73..df226fa63c728d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -623,6 +623,7 @@ PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals) EVAL_CALL_STAT_INC(EVAL_CALL_LEGACY); PyObject *res = _PyEval_Vector(tstate, func, locals, NULL, 0, NULL); Py_DECREF(func); + Py_DECREF(builtins); return res; } @@ -1946,6 +1947,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, Py_XDECREF(kwnames); PyMem_Free(newargs); Py_DECREF(defaults); + Py_DECREF(builtins); return res; } From c5b38b08eb2eddad75b0f87d1d2ae5c21ea49e2f Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 15:46:03 +0800 Subject: [PATCH 10/49] release reference to builtins module --- Objects/frameobject.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index c07cb00630fbcb..6348774a5550a2 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -2105,18 +2105,21 @@ PyFrame_GetGenerator(PyFrameObject *frame) PyObject* _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) { - PyObject *builtins; + PyObject *maybe_builtins; int has_builtins = PyMapping_GetOptionalItem( - globals, &_Py_ID(__builtins__), &builtins); + globals, &_Py_ID(__builtins__), &maybe_builtins); if (has_builtins < 0) { return NULL; } if (has_builtins) { - if (PyModule_Check(builtins)) { - builtins = Py_XNewRef(_PyModule_GetDict(builtins)); + PyObject *builtins; + if (PyModule_Check(maybe_builtins)) { + builtins = Py_XNewRef(_PyModule_GetDict(maybe_builtins)); + Py_DECREF(maybe_builtins); assert(builtins != NULL); + return builtins; } - return builtins; + return maybe_builtins; } return Py_NewRef(_PyEval_GetBuiltins(tstate)); } From 0daa66bb16769592c0dfbb9cd941a3f7d6d64ae9 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 15:47:03 +0800 Subject: [PATCH 11/49] streamline code --- Objects/frameobject.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 6348774a5550a2..32dbbbf1c17c8c 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -2112,9 +2112,8 @@ _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) return NULL; } if (has_builtins) { - PyObject *builtins; if (PyModule_Check(maybe_builtins)) { - builtins = Py_XNewRef(_PyModule_GetDict(maybe_builtins)); + PyObject *builtins = Py_XNewRef(_PyModule_GetDict(maybe_builtins)); Py_DECREF(maybe_builtins); assert(builtins != NULL); return builtins; From 2852b8f7f2a158e0914d7a75ef2cc270ed691361 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 18:18:42 +0800 Subject: [PATCH 12/49] added dict-specific path for insertion of __builtins__ into globals --- Python/bltinmodule.c | 31 +++++++++++++++++++++++++------ Python/pythonrun.c | 28 +++++++++++++++++++++------- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index af5fbaabcf6980..33a249f936ebdb 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -991,9 +991,18 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, goto error; } - int r = PyMapping_HasKeyStringWithError(globals, "__builtins__"); - if (r == 0) { - r = PyMapping_SetItemString(globals, "__builtins__", PyEval_GetBuiltins()); + int r; + if (PyDict_Check(globals)) { + r = PyDict_Contains(globals, &_Py_ID(__builtins__)); + if (r == 0) { + r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); + } + } + else { + r = PyMapping_HasKeyWithError(globals, &_Py_ID(__builtins__)); + if (r == 0) { + r = PyObject_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); + } } if (r < 0) { goto error; @@ -1093,9 +1102,19 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, Py_TYPE(locals)->tp_name); goto error; } - int r = PyMapping_HasKeyStringWithError(globals, "__builtins__"); - if (r == 0) { - r = PyMapping_SetItemString(globals, "__builtins__", PyEval_GetBuiltins()); + + int r; + if (PyDict_Check(globals)) { + r = PyDict_Contains(globals, &_Py_ID(__builtins__)); + if (r == 0) { + r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); + } + } + else { + r = PyMapping_HasKeyWithError(globals, &_Py_ID(__builtins__)); + if (r == 0) { + r = PyObject_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); + } } if (r < 0) { goto error; diff --git a/Python/pythonrun.c b/Python/pythonrun.c index cd47ef3757eb20..3975da686b513c 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1277,16 +1277,30 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py PyErr_SetString(PyExc_SystemError, "globals must be a mapping"); return NULL; } - int has_builtins = PyMapping_HasKeyStringWithError(globals, "__builtins__"); - if (has_builtins < 0) { - return NULL; + int has_builtins; + if (PyDict_Check(globals)) { + has_builtins = PyDict_ContainsString(globals, "__builtins__"); + if (has_builtins < 0) { + return NULL; + } + if (!has_builtins) { + if (PyDict_SetItemString(globals, "__builtins__", + tstate->interp->builtins) < 0) { + return NULL; + } + } } - if (!has_builtins) { - if (PyMapping_SetItemString(globals, "__builtins__", - tstate->interp->builtins) < 0) - { + else { + has_builtins = PyMapping_HasKeyStringWithError(globals, "__builtins__"); + if (has_builtins < 0) { return NULL; } + if (!has_builtins) { + if (PyMapping_SetItemString(globals, "__builtins__", + tstate->interp->builtins) < 0) { + return NULL; + } + } } v = PyEval_EvalCode((PyObject*)co, globals, locals); From 5120260fcdc772d918765a516eeec8df8053704a Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 14:31:21 +0000 Subject: [PATCH 13/49] try PyDict_* before PyMapping_* --- Python/bytecodes.c | 43 +++++++++++++++++++++++++------------- Python/executor_cases.c.h | 25 +++++++++++++++------- Python/generated_cases.c.h | 43 +++++++++++++++++++++++++------------- 3 files changed, 75 insertions(+), 36 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 61850aff1ab722..bed4a4f131c95a 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1465,23 +1465,32 @@ dummy_func( } inst(STORE_GLOBAL, (v --)) { + PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyObject_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); + PyObject *value = PyStackRef_AsPyObjectBorrow(v); + int err = PyDict_SetItem(globals, name, value); + if (err < 0) { + err = PyObject_SetItem(globals, name, value); + } DECREF_INPUTS(); ERROR_IF(err, error); } inst(DELETE_GLOBAL, (--)) { + PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyMapping_DelItem(GLOBALS(), name); - // Can't use ERROR_IF here. + int err = PyDict_Pop(globals, name, NULL); if (err < 0) { - if (_PyErr_Occurred(tstate) && - _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); + err = PyMapping_DelItem(globals, name); + // Can't use ERROR_IF here. + if (err < 0) { + if (_PyErr_Occurred(tstate) && + _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + } + ERROR_NO_POP(); } - ERROR_NO_POP(); } } @@ -1540,6 +1549,7 @@ dummy_func( inst(LOAD_NAME, (-- v)) { PyObject *v_o; + PyObject *globals = GLOBALS(); PyObject *mod_or_class_dict = LOCALS(); if (mod_or_class_dict == NULL) { _PyErr_SetString(tstate, PyExc_SystemError, @@ -1551,18 +1561,23 @@ dummy_func( ERROR_NO_POP(); } if (v_o == NULL) { - if (PyMapping_GetOptionalItem(GLOBALS(), name, &v_o) < 0) { + if (PyDict_GetItemRef(globals, name, &v_o) < 0) { ERROR_NO_POP(); } if (v_o == NULL) { - if (PyMapping_GetOptionalItem(BUILTINS(), name, &v_o) < 0) { + if (PyMapping_GetOptionalItem(globals, name, &v_o) < 0) { ERROR_NO_POP(); } if (v_o == NULL) { - _PyEval_FormatExcCheckArg( - tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - ERROR_NO_POP(); + if (PyMapping_GetOptionalItem(BUILTINS(), name, &v_o) < 0) { + ERROR_NO_POP(); + } + if (v_o == NULL) { + _PyEval_FormatExcCheckArg( + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + ERROR_NO_POP(); + } } } } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 6cf910afc52ad5..db754f5f0f834f 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1509,8 +1509,13 @@ _PyStackRef v; oparg = CURRENT_OPARG(); v = stack_pointer[-1]; + PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyObject_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); + PyObject *value = PyStackRef_AsPyObjectBorrow(v); + int err = PyDict_SetItem(globals, name, value); + if (err < 0) { + err = PyObject_SetItem(globals, name, value); + } PyStackRef_CLOSE(v); if (err) JUMP_TO_ERROR(); stack_pointer += -1; @@ -1520,16 +1525,20 @@ case _DELETE_GLOBAL: { oparg = CURRENT_OPARG(); + PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyMapping_DelItem(GLOBALS(), name); - // Can't use ERROR_IF here. + int err = PyDict_Pop(globals, name, NULL); if (err < 0) { - if (_PyErr_Occurred(tstate) && - _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); + err = PyMapping_DelItem(globals, name); + // Can't use ERROR_IF here. + if (err < 0) { + if (_PyErr_Occurred(tstate) && + _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + } + JUMP_TO_ERROR(); } - JUMP_TO_ERROR(); } break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 760d759402e1bb..b5ef5c1af2692d 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2777,16 +2777,20 @@ frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(DELETE_GLOBAL); + PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyMapping_DelItem(GLOBALS(), name); - // Can't use ERROR_IF here. + int err = PyDict_Pop(globals, name, NULL); if (err < 0) { - if (_PyErr_Occurred(tstate) && - _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); + err = PyMapping_DelItem(globals, name); + // Can't use ERROR_IF here. + if (err < 0) { + if (_PyErr_Occurred(tstate) && + _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + } + goto error; } - goto error; } DISPATCH(); } @@ -4974,6 +4978,7 @@ INSTRUCTION_STATS(LOAD_NAME); _PyStackRef v; PyObject *v_o; + PyObject *globals = GLOBALS(); PyObject *mod_or_class_dict = LOCALS(); if (mod_or_class_dict == NULL) { _PyErr_SetString(tstate, PyExc_SystemError, @@ -4985,18 +4990,23 @@ goto error; } if (v_o == NULL) { - if (PyMapping_GetOptionalItem(GLOBALS(), name, &v_o) < 0) { + if (PyDict_GetItemRef(globals, name, &v_o) < 0) { goto error; } if (v_o == NULL) { - if (PyMapping_GetOptionalItem(BUILTINS(), name, &v_o) < 0) { + if (PyMapping_GetOptionalItem(globals, name, &v_o) < 0) { goto error; } if (v_o == NULL) { - _PyEval_FormatExcCheckArg( - tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - goto error; + if (PyMapping_GetOptionalItem(BUILTINS(), name, &v_o) < 0) { + goto error; + } + if (v_o == NULL) { + _PyEval_FormatExcCheckArg( + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + goto error; + } } } } @@ -6182,8 +6192,13 @@ INSTRUCTION_STATS(STORE_GLOBAL); _PyStackRef v; v = stack_pointer[-1]; + PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyObject_SetItem(GLOBALS(), name, PyStackRef_AsPyObjectBorrow(v)); + PyObject *value = PyStackRef_AsPyObjectBorrow(v); + int err = PyDict_SetItem(globals, name, value); + if (err < 0) { + err = PyObject_SetItem(globals, name, value); + } PyStackRef_CLOSE(v); if (err) goto pop_1_error; stack_pointer += -1; From 723d665c8ebadab4454c11b0e4441309f6302a96 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 15:00:27 +0000 Subject: [PATCH 14/49] switch from EAFP to LBYL --- Python/bytecodes.c | 51 +++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index bed4a4f131c95a..4b5ef47becc122 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1468,8 +1468,11 @@ dummy_func( PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); PyObject *value = PyStackRef_AsPyObjectBorrow(v); - int err = PyDict_SetItem(globals, name, value); - if (err < 0) { + int err; + if (PyDict_Check(globals)) { + err = PyDict_SetItem(globals, name, value); + } + else { err = PyObject_SetItem(globals, name, value); } DECREF_INPUTS(); @@ -1479,18 +1482,20 @@ dummy_func( inst(DELETE_GLOBAL, (--)) { PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyDict_Pop(globals, name, NULL); - if (err < 0) { + int err; + if (PyDict_Check(globals)) { + err = PyDict_Pop(globals, name, NULL); + else { err = PyMapping_DelItem(globals, name); - // Can't use ERROR_IF here. - if (err < 0) { - if (_PyErr_Occurred(tstate) && - _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - } - ERROR_NO_POP(); + } + // Can't use ERROR_IF here. + if (err < 0) { + if (_PyErr_Occurred(tstate) && + _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); } + ERROR_NO_POP(); } } @@ -1561,23 +1566,23 @@ dummy_func( ERROR_NO_POP(); } if (v_o == NULL) { - if (PyDict_GetItemRef(globals, name, &v_o) < 0) { + if (PyDict_Check(globals)) { + if (PyDict_GetItemRef(globals, name, &v_o) < 0) { + ERROR_NO_POP(); + } + } + else if (PyMapping_GetOptionalItem(globals, name, &v_o) < 0) { ERROR_NO_POP(); } if (v_o == NULL) { - if (PyMapping_GetOptionalItem(globals, name, &v_o) < 0) { + if (PyMapping_GetOptionalItem(BUILTINS(), name, &v_o) < 0) { ERROR_NO_POP(); } if (v_o == NULL) { - if (PyMapping_GetOptionalItem(BUILTINS(), name, &v_o) < 0) { - ERROR_NO_POP(); - } - if (v_o == NULL) { - _PyEval_FormatExcCheckArg( - tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - ERROR_NO_POP(); - } + _PyEval_FormatExcCheckArg( + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + ERROR_NO_POP(); } } } From 3908ee8b50a7b5d224e85d66c691d72f9f2cfe93 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 15:07:10 +0000 Subject: [PATCH 15/49] fixed typo --- Python/bytecodes.c | 2 +- Python/executor_cases.c.h | 29 +++++++++++++--------- Python/generated_cases.c.h | 51 +++++++++++++++++++++----------------- 3 files changed, 46 insertions(+), 36 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 4b5ef47becc122..eac7a226a6359e 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1485,7 +1485,7 @@ dummy_func( int err; if (PyDict_Check(globals)) { err = PyDict_Pop(globals, name, NULL); - else { + } else { err = PyMapping_DelItem(globals, name); } // Can't use ERROR_IF here. diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index db754f5f0f834f..4b4a21f4bc16f1 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1512,8 +1512,11 @@ PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); PyObject *value = PyStackRef_AsPyObjectBorrow(v); - int err = PyDict_SetItem(globals, name, value); - if (err < 0) { + int err; + if (PyDict_Check(globals)) { + err = PyDict_SetItem(globals, name, value); + } + else { err = PyObject_SetItem(globals, name, value); } PyStackRef_CLOSE(v); @@ -1527,18 +1530,20 @@ oparg = CURRENT_OPARG(); PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyDict_Pop(globals, name, NULL); - if (err < 0) { + int err; + if (PyDict_Check(globals)) { + err = PyDict_Pop(globals, name, NULL); + } else { err = PyMapping_DelItem(globals, name); - // Can't use ERROR_IF here. - if (err < 0) { - if (_PyErr_Occurred(tstate) && - _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - } - JUMP_TO_ERROR(); + } + // Can't use ERROR_IF here. + if (err < 0) { + if (_PyErr_Occurred(tstate) && + _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); } + JUMP_TO_ERROR(); } break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index b5ef5c1af2692d..68a55d1e9fd380 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2779,18 +2779,20 @@ INSTRUCTION_STATS(DELETE_GLOBAL); PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - int err = PyDict_Pop(globals, name, NULL); - if (err < 0) { + int err; + if (PyDict_Check(globals)) { + err = PyDict_Pop(globals, name, NULL); + } else { err = PyMapping_DelItem(globals, name); - // Can't use ERROR_IF here. - if (err < 0) { - if (_PyErr_Occurred(tstate) && - _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { - _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - } - goto error; + } + // Can't use ERROR_IF here. + if (err < 0) { + if (_PyErr_Occurred(tstate) && + _PyErr_ExceptionMatches(tstate, PyExc_KeyError)) { + _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, + NAME_ERROR_MSG, name); } + goto error; } DISPATCH(); } @@ -4990,23 +4992,23 @@ goto error; } if (v_o == NULL) { - if (PyDict_GetItemRef(globals, name, &v_o) < 0) { + if (PyDict_Check(globals)) { + if (PyDict_GetItemRef(globals, name, &v_o) < 0) { + goto error; + } + } + else if (PyMapping_GetOptionalItem(globals, name, &v_o) < 0) { goto error; } if (v_o == NULL) { - if (PyMapping_GetOptionalItem(globals, name, &v_o) < 0) { + if (PyMapping_GetOptionalItem(BUILTINS(), name, &v_o) < 0) { goto error; } if (v_o == NULL) { - if (PyMapping_GetOptionalItem(BUILTINS(), name, &v_o) < 0) { - goto error; - } - if (v_o == NULL) { - _PyEval_FormatExcCheckArg( - tstate, PyExc_NameError, - NAME_ERROR_MSG, name); - goto error; - } + _PyEval_FormatExcCheckArg( + tstate, PyExc_NameError, + NAME_ERROR_MSG, name); + goto error; } } } @@ -6195,8 +6197,11 @@ PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); PyObject *value = PyStackRef_AsPyObjectBorrow(v); - int err = PyDict_SetItem(globals, name, value); - if (err < 0) { + int err; + if (PyDict_Check(globals)) { + err = PyDict_SetItem(globals, name, value); + } + else { err = PyObject_SetItem(globals, name, value); } PyStackRef_CLOSE(v); From 4a14682496d2ad5acbaf4213bec8873c3e121744 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 15:29:54 +0000 Subject: [PATCH 16/49] dict-specific path for function --- Objects/funcobject.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 7dfff4ae9cfe31..4f374bc1c0ae62 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -103,7 +103,16 @@ PyFunctionObject * _PyFunction_FromConstructor(PyFrameConstructor *constr) { PyObject *module; - if (PyMapping_GetOptionalItemString(constr->fc_globals, "__name__", &module) < 0) { + int r; + if (PyDict_Check(constr->fc_globals)) { + r = PyDict_GetItemRef(constr->fc_globals, &_Py_ID(__name__), + &module); + } + else { + r = PyMapping_GetOptionalItem(constr->fc_globals, &_Py_ID(__name__), + &module); + } + if (r < 0) { return NULL; } @@ -141,7 +150,7 @@ PyObject * PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname) { assert(globals != NULL); - assert(PyDict_Check(globals)); + assert(PyDict_Check(globals) || PyMapping_Check(globals)); Py_INCREF(globals); PyThreadState *tstate = _PyThreadState_GET(); @@ -174,7 +183,14 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname // __module__: Use globals['__name__'] if it exists, or NULL. PyObject *module; PyObject *builtins = NULL; - if (PyMapping_GetOptionalItemString(globals, "__name__", &module) < 0) { + int r; + if (PyDict_Check(globals)) { + r = PyDict_GetItemRef(globals, &_Py_ID(__name__), &module); + } + else { + r = PyMapping_GetOptionalItemString(globals, "__name__", &module); + } + if (r < 0) { goto error; } From ab2974e50c83adacffefeec7b108cc954b4ba596 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 15:45:07 +0000 Subject: [PATCH 17/49] more dict-specific paths --- Objects/frameobject.c | 11 +++++++++-- Python/ceval.c | 8 +++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 32dbbbf1c17c8c..b95275378ea19a 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -2106,8 +2106,15 @@ PyObject* _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) { PyObject *maybe_builtins; - int has_builtins = PyMapping_GetOptionalItem( - globals, &_Py_ID(__builtins__), &maybe_builtins); + int has_builtins; + if (PyDict_Check(globals)) { + has_builtins = PyDict_GetItemRef(globals, + &_Py_ID(__builtins__), &maybe_builtins); + } + else { + has_builtins = PyMapping_GetOptionalItem(globals, + &_Py_ID(__builtins__), &maybe_builtins); + } if (has_builtins < 0) { return NULL; } diff --git a/Python/ceval.c b/Python/ceval.c index df226fa63c728d..e25f8e8afdc99a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -205,7 +205,13 @@ maybe_lltrace_resume_frame(_PyInterpreterFrame *frame, _PyInterpreterFrame *skip if (frame == skip_frame) { return 0; } - int r = PyMapping_HasKeyWithError(globals, &_Py_ID(__lltrace__)); + int r; + if (PyDict_Check(globals)) { + r = PyDict_Contains(globals, &_Py_ID(__lltrace__)); + } + else { + r = PyMapping_HasKeyWithError(globals, &_Py_ID(__lltrace__)); + } if (r < 0) { return -1; } From 1218ab71c262d4bed2efcd8170b4dded9a114ca9 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 5 Jul 2024 15:56:28 +0000 Subject: [PATCH 18/49] minor adjustments --- Objects/funcobject.c | 2 +- Python/bltinmodule.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 4f374bc1c0ae62..fb1ff94411e3e6 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -188,7 +188,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname r = PyDict_GetItemRef(globals, &_Py_ID(__name__), &module); } else { - r = PyMapping_GetOptionalItemString(globals, "__name__", &module); + r = PyMapping_GetOptionalItem(globals, &_Py_ID(__name__), &module); } if (r < 0) { goto error; diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 33a249f936ebdb..9bef9f6186fd77 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1091,7 +1091,7 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, Py_INCREF(locals); } - if (!PyMapping_Check(globals)) { + if (!PyDict_Check(globals) && !PyMapping_Check(globals)) { PyErr_Format(PyExc_TypeError, "globals must be a mapping or None, not %.100s", Py_TYPE(globals)->tp_name); goto error; From 375af2b7136b05d7de49f4bc99feaf3fe2ee278a Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 16:24:20 +0000 Subject: [PATCH 19/49] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst new file mode 100644 index 00000000000000..50ed3e3c2c5d7c --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst @@ -0,0 +1 @@ +Allow a mapping to be used as a global namespace, applicable to frame and function objects, and built-in functions :func:`exec` and :func:`eval`. From 06b225b40481a4171ec909e750efc3fd5e3acef2 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Mon, 8 Jul 2024 09:21:37 +0800 Subject: [PATCH 20/49] removed redundant PyDict_Check; streamlined code --- Objects/funcobject.c | 5 +++-- Python/bltinmodule.c | 14 ++++++++------ Python/bytecodes.c | 7 ++++--- Python/executor_cases.c.h | 7 ++++--- Python/generated_cases.c.h | 7 ++++--- Python/pythonrun.c | 10 +++++----- 6 files changed, 28 insertions(+), 22 deletions(-) diff --git a/Objects/funcobject.c b/Objects/funcobject.c index fb1ff94411e3e6..16b2759e6f89e6 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -150,7 +150,6 @@ PyObject * PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname) { assert(globals != NULL); - assert(PyDict_Check(globals) || PyMapping_Check(globals)); Py_INCREF(globals); PyThreadState *tstate = _PyThreadState_GET(); @@ -187,8 +186,10 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname if (PyDict_Check(globals)) { r = PyDict_GetItemRef(globals, &_Py_ID(__name__), &module); } - else { + else if (PyMapping_Check(globals)) { r = PyMapping_GetOptionalItem(globals, &_Py_ID(__name__), &module); + } else { + goto error; } if (r < 0) { goto error; diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 9bef9f6186fd77..a84e8d14c67f94 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -960,11 +960,9 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, const char *str; if (locals != Py_None && !PyMapping_Check(locals)) { - PyErr_SetString(PyExc_TypeError, "locals must be a mapping"); - return NULL; - } - if (globals != Py_None && !PyMapping_Check(globals)) { - PyErr_SetString(PyExc_TypeError, "globals must be a mapping"); + PyErr_Format(PyExc_TypeError, + "locals must be a mapping or None, not %.100s", + Py_TYPE(locals)->tp_name); return NULL; } if (globals == Py_None) { @@ -998,11 +996,15 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); } } - else { + else if (PyMapping_Check(globals)) { r = PyMapping_HasKeyWithError(globals, &_Py_ID(__builtins__)); if (r == 0) { r = PyObject_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); } + } else { + PyErr_Format(PyExc_TypeError, "globals must be a mapping or None, not %.100s", + Py_TYPE(globals)->tp_name); + return NULL; } if (r < 0) { goto error; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index eac7a226a6359e..cfe524577db7d6 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1467,13 +1467,14 @@ dummy_func( inst(STORE_GLOBAL, (v --)) { PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - PyObject *value = PyStackRef_AsPyObjectBorrow(v); int err; if (PyDict_Check(globals)) { - err = PyDict_SetItem(globals, name, value); + err = PyDict_SetItem(globals, name, + PyStackRef_AsPyObjectBorrow(v)); } else { - err = PyObject_SetItem(globals, name, value); + err = PyObject_SetItem(globals, name, + PyStackRef_AsPyObjectBorrow(v)); } DECREF_INPUTS(); ERROR_IF(err, error); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 4b4a21f4bc16f1..ec61b57825f042 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1511,13 +1511,14 @@ v = stack_pointer[-1]; PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - PyObject *value = PyStackRef_AsPyObjectBorrow(v); int err; if (PyDict_Check(globals)) { - err = PyDict_SetItem(globals, name, value); + err = PyDict_SetItem(globals, name, + PyStackRef_AsPyObjectBorrow(v)); } else { - err = PyObject_SetItem(globals, name, value); + err = PyObject_SetItem(globals, name, + PyStackRef_AsPyObjectBorrow(v)); } PyStackRef_CLOSE(v); if (err) JUMP_TO_ERROR(); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 68a55d1e9fd380..f3b7302f493bad 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -6196,13 +6196,14 @@ v = stack_pointer[-1]; PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); - PyObject *value = PyStackRef_AsPyObjectBorrow(v); int err; if (PyDict_Check(globals)) { - err = PyDict_SetItem(globals, name, value); + err = PyDict_SetItem(globals, name, + PyStackRef_AsPyObjectBorrow(v)); } else { - err = PyObject_SetItem(globals, name, value); + err = PyObject_SetItem(globals, name, + PyStackRef_AsPyObjectBorrow(v)); } PyStackRef_CLOSE(v); if (err) goto pop_1_error; diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 3975da686b513c..ac813df2d43ec4 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1273,10 +1273,6 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py _PyRuntime.signals.unhandled_keyboard_interrupt = 0; /* Set globals['__builtins__'] if it doesn't exist */ - if (!globals || !PyMapping_Check(globals)) { - PyErr_SetString(PyExc_SystemError, "globals must be a mapping"); - return NULL; - } int has_builtins; if (PyDict_Check(globals)) { has_builtins = PyDict_ContainsString(globals, "__builtins__"); @@ -1290,7 +1286,7 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py } } } - else { + else if (PyMapping_Check(globals)) { has_builtins = PyMapping_HasKeyStringWithError(globals, "__builtins__"); if (has_builtins < 0) { return NULL; @@ -1302,6 +1298,10 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py } } } + else { + PyErr_SetString(PyExc_SystemError, "globals must be a mapping"); + return NULL; + } v = PyEval_EvalCode((PyObject*)co, globals, locals); if (!v && _PyErr_Occurred(tstate) == PyExc_KeyboardInterrupt) { From d5849879bba1f119e21acde8ddd5187cde94002a Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Mon, 8 Jul 2024 10:27:21 +0800 Subject: [PATCH 21/49] fixed check for null globals --- Python/bltinmodule.c | 11 +++++------ Python/pythonrun.c | 6 +++++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index a84e8d14c67f94..e63625307a0678 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1093,11 +1093,6 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, Py_INCREF(locals); } - if (!PyDict_Check(globals) && !PyMapping_Check(globals)) { - PyErr_Format(PyExc_TypeError, "globals must be a mapping or None, not %.100s", - Py_TYPE(globals)->tp_name); - goto error; - } if (!PyMapping_Check(locals)) { PyErr_Format(PyExc_TypeError, "locals must be a mapping or None, not %.100s", @@ -1112,11 +1107,15 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); } } - else { + else if (PyMapping_Check(globals)) { r = PyMapping_HasKeyWithError(globals, &_Py_ID(__builtins__)); if (r == 0) { r = PyObject_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); } + } else { + PyErr_Format(PyExc_TypeError, "globals must be a mapping or None, not %.100s", + Py_TYPE(globals)->tp_name); + goto error; } if (r < 0) { goto error; diff --git a/Python/pythonrun.c b/Python/pythonrun.c index ac813df2d43ec4..ca1b1d8488c49e 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1274,7 +1274,10 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py /* Set globals['__builtins__'] if it doesn't exist */ int has_builtins; - if (PyDict_Check(globals)) { + if (!globals) { + goto error; + } + else if (PyDict_Check(globals)) { has_builtins = PyDict_ContainsString(globals, "__builtins__"); if (has_builtins < 0) { return NULL; @@ -1299,6 +1302,7 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py } } else { +error: PyErr_SetString(PyExc_SystemError, "globals must be a mapping"); return NULL; } From 3d3a5630404ff5ccdbb9c386b9b76b449fd41057 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Mon, 8 Jul 2024 16:04:45 +0800 Subject: [PATCH 22/49] updated docs --- Doc/library/functions.rst | 33 +++++++++++-------- Doc/reference/datamodel.rst | 6 ++-- ...-07-05-16-24-14.gh-issue-121306.nUBgho.rst | 2 +- Objects/funcobject.c | 15 +++------ 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 1d82f92ea67857..4c21f0474c3ae0 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -581,7 +581,7 @@ are always available. They are listed here in alphabetical order. :param globals: The global namespace (default: ``None``). - :type globals: :class:`dict` | ``None`` + :type globals: :term:`mapping` | ``None`` :param locals: The local namespace (default: ``None``). @@ -592,14 +592,14 @@ are always available. They are listed here in alphabetical order. The *expression* argument is parsed and evaluated as a Python expression (technically speaking, a condition list) using the *globals* and *locals* - mappings as global and local namespace. If the *globals* dictionary is + mappings as global and local namespace. If the *globals* mapping is present and does not contain a value for the key ``__builtins__``, a reference to the dictionary of the built-in module :mod:`builtins` is inserted under that key before *expression* is parsed. That way you can control what builtins are available to the executed code by inserting your - own ``__builtins__`` dictionary into *globals* before passing it to + own ``__builtins__`` mapping into *globals* before passing it to :func:`eval`. If the *locals* mapping is omitted it defaults to the - *globals* dictionary. If both mappings are omitted, the expression is + *globals* mapping. If both mappings are omitted, the expression is executed with the *globals* and *locals* in the environment where :func:`eval` is called. Note, *eval()* will only have access to the :term:`nested scopes ` (non-locals) in the enclosing @@ -619,7 +619,7 @@ are always available. They are listed here in alphabetical order. Hints: dynamic execution of statements is supported by the :func:`exec` function. The :func:`globals` and :func:`locals` functions - return the current global and local dictionary, respectively, which may be + return the current global and local mapping, respectively, which may be useful to pass around for use by :func:`eval` or :func:`exec`. If the given source is a string, then leading and trailing spaces and tabs @@ -642,6 +642,10 @@ are always available. They are listed here in alphabetical order. The semantics of the default *locals* namespace have been adjusted as described for the :func:`locals` builtin. + .. versionchanged:: 3.14 + + A mapping can now be passed as the *globals* argument. + .. index:: pair: built-in function; exec .. function:: exec(source, /, globals=None, locals=None, *, closure=None) @@ -658,12 +662,12 @@ are always available. They are listed here in alphabetical order. :func:`exec` function. The return value is ``None``. In all cases, if the optional parts are omitted, the code is executed in the - current scope. If only *globals* is provided, it must be a dictionary - (and not a subclass of dictionary), which + current scope. If only *globals* is provided, it must be a mapping, which will be used for both the global and the local variables. If *globals* and *locals* are given, they are used for the global and local variables, - respectively. If provided, *locals* can be any mapping object. Remember - that at the module level, globals and locals are the same dictionary. + respectively. If provided, *globals* and *locals* can be any mapping + objects. Remember that at the module level, globals and locals are the same + mapping. .. note:: @@ -673,11 +677,11 @@ are always available. They are listed here in alphabetical order. to access variables assigned at the top level (as the "top level" variables are treated as class variables in a class definition). - If the *globals* dictionary does not contain a value for the key - ``__builtins__``, a reference to the dictionary of the built-in module + If the *globals* mapping does not contain a value for the key + ``__builtins__``, a reference to the mapping of the built-in module :mod:`builtins` is inserted under that key. That way you can control what builtins are available to the executed code by inserting your own - ``__builtins__`` dictionary into *globals* before passing it to :func:`exec`. + ``__builtins__`` mapping into *globals* before passing it to :func:`exec`. The *closure* argument specifies a closure--a tuple of cellvars. It's only valid when the *object* is a code object containing free variables. @@ -698,7 +702,7 @@ are always available. They are listed here in alphabetical order. .. note:: The default *locals* act as described for function :func:`locals` below. - Pass an explicit *locals* dictionary if you need to see effects of the + Pass an explicit *locals* mapping if you need to see effects of the code on *locals* after function :func:`exec` returns. .. versionchanged:: 3.11 @@ -713,6 +717,9 @@ are always available. They are listed here in alphabetical order. The semantics of the default *locals* namespace have been adjusted as described for the :func:`locals` builtin. + .. versionchanged:: 3.14 + + A mapping can now be passed as the *globals* argument. .. function:: filter(function, iterable) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 34d8bbee4c6806..469221162eee3a 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -555,7 +555,7 @@ Special read-only attributes - Meaning * - .. attribute:: function.__globals__ - - A reference to the :class:`dictionary ` that holds the function's + - A reference to the :term:`mapping` that holds the function's :ref:`global variables ` -- the global namespace of the module in which the function was defined. @@ -1356,11 +1356,11 @@ Special read-only attributes Return a proxy for optimized scopes. * - .. attribute:: frame.f_globals - - The dictionary used by the frame to look up + - The mapping used by the frame to look up :ref:`global variables ` * - .. attribute:: frame.f_builtins - - The dictionary used by the frame to look up + - The mapping used by the frame to look up :ref:`built-in (intrinsic) names ` * - .. attribute:: frame.f_lasti diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst index 50ed3e3c2c5d7c..330a3d9d3fd741 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst @@ -1 +1 @@ -Allow a mapping to be used as a global namespace, applicable to frame and function objects, and built-in functions :func:`exec` and :func:`eval`. +Allow a mapping to be passed as the ``globals`` argument for the built-in functions ``exec`` and ``eval``. diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 16b2759e6f89e6..e392352722b457 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -150,6 +150,7 @@ PyObject * PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname) { assert(globals != NULL); + assert(PyDict_Check(globals)); Py_INCREF(globals); PyThreadState *tstate = _PyThreadState_GET(); @@ -182,23 +183,15 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname // __module__: Use globals['__name__'] if it exists, or NULL. PyObject *module; PyObject *builtins = NULL; - int r; - if (PyDict_Check(globals)) { - r = PyDict_GetItemRef(globals, &_Py_ID(__name__), &module); - } - else if (PyMapping_Check(globals)) { - r = PyMapping_GetOptionalItem(globals, &_Py_ID(__name__), &module); - } else { - goto error; - } - if (r < 0) { + if (PyDict_GetItemRef(globals, &_Py_ID(__name__), &module) < 0) { goto error; } - builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); + builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); // borrowed ref if (builtins == NULL) { goto error; } + Py_INCREF(builtins); PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); if (op == NULL) { From 8bbc34c554cd0a38e24e0c9ce25f5cbd606bd1c4 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Mon, 8 Jul 2024 16:05:27 +0800 Subject: [PATCH 23/49] minor update to docs --- Doc/reference/datamodel.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 469221162eee3a..ae0d49ff5f05d3 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -555,7 +555,7 @@ Special read-only attributes - Meaning * - .. attribute:: function.__globals__ - - A reference to the :term:`mapping` that holds the function's + - A reference to the :class:`dictionary ` that holds the function's :ref:`global variables ` -- the global namespace of the module in which the function was defined. From ca2ea128dc02d38e475c5e9f97ef57a00f4460af Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Mon, 8 Jul 2024 16:09:15 +0800 Subject: [PATCH 24/49] updated tests --- Lib/test/test_capi/test_run.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_capi/test_run.py b/Lib/test/test_capi/test_run.py index ddaa7d981389c3..db2fd40c4dd969 100644 --- a/Lib/test/test_capi/test_run.py +++ b/Lib/test/test_capi/test_run.py @@ -66,7 +66,7 @@ def run(s, *args): self.assertRaises(SystemError, run, b'a\n', NULL, dict(a=1)) self.assertRaises(NameError, run, b'a\n', UserDict()) self.assertRaises(NameError, run, b'a\n', UserDict(), {}) - run(b'a\n', UserDict(), dict(a=1)) + self.assertIsNone(run(b'a\n', UserDict(), dict(a=1))) # CRASHES run(NULL, {}) @@ -99,7 +99,7 @@ def run(*args): self.assertRaises(SystemError, run, NULL, dict(a=1)) self.assertRaises(NameError, run, UserDict()) self.assertRaises(NameError, run, UserDict(), {}) - run(UserDict(), dict(a=1)) + self.assertIsNone(run(UserDict(), dict(a=1))) @unittest.skipUnless(TESTFN_UNDECODABLE, 'only works if there are undecodable paths') @unittest.skipIf(os.name == 'nt', 'does not work on Windows') From 0f9e2898864bd2cbf4e246fdb6ab6a23611cff21 Mon Sep 17 00:00:00 2001 From: blhsing Date: Mon, 8 Jul 2024 16:15:58 +0800 Subject: [PATCH 25/49] Update 2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst --- .../2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst index 330a3d9d3fd741..603e98c5adfe66 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst @@ -1 +1 @@ -Allow a mapping to be passed as the ``globals`` argument for the built-in functions ``exec`` and ``eval``. +Allow a mapping to be passed as the *globals* argument for the built-in functions :func:`exec` and :func:`eval`. From 186ee55d6c141fccc5b03471b9c7efe93c87eddf Mon Sep 17 00:00:00 2001 From: blhsing Date: Mon, 8 Jul 2024 16:16:16 +0800 Subject: [PATCH 26/49] Update 2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst --- .../2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst index 603e98c5adfe66..34da6bdcbfdefe 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst @@ -1 +1,2 @@ Allow a mapping to be passed as the *globals* argument for the built-in functions :func:`exec` and :func:`eval`. +Patch by Ben Hsing From 190c09fc2b3ae3115f090d4b24e7f54ffbc00e3f Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Mon, 8 Jul 2024 16:33:23 +0800 Subject: [PATCH 27/49] fixed reference count --- Doc/reference/datamodel.rst | 3 +++ Objects/funcobject.c | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index ae0d49ff5f05d3..02beb1f5657be3 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1359,6 +1359,9 @@ Special read-only attributes - The mapping used by the frame to look up :ref:`global variables ` + .. versionchanged:: 3.14 + May now be a mapping passed to :func:`exec` or :func:`eval`. + * - .. attribute:: frame.f_builtins - The mapping used by the frame to look up :ref:`built-in (intrinsic) names ` diff --git a/Objects/funcobject.c b/Objects/funcobject.c index e392352722b457..e8b5d13e535195 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -187,11 +187,10 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname goto error; } - builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); // borrowed ref + builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); if (builtins == NULL) { goto error; } - Py_INCREF(builtins); PyFunctionObject *op = PyObject_GC_New(PyFunctionObject, &PyFunction_Type); if (op == NULL) { From 1949be7461815dd8a5f2ec92e6fbca571b333b1d Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Tue, 9 Jul 2024 13:39:08 +0800 Subject: [PATCH 28/49] updated test to validate evaluated result --- Lib/test/test_capi/test_eval_code_ex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_capi/test_eval_code_ex.py b/Lib/test/test_capi/test_eval_code_ex.py index 181f63734e2ae8..92ba9524ca1df5 100644 --- a/Lib/test/test_capi/test_eval_code_ex.py +++ b/Lib/test/test_capi/test_eval_code_ex.py @@ -21,9 +21,9 @@ def f(): eval_code_ex = _testcapi.eval_code_ex code = f.__code__ self.assertEqual(eval_code_ex(code, dict(a=1)), 1) + self.assertEqual(eval_code_ex(code, UserDict(a=1)), 1) self.assertRaises(NameError, eval_code_ex, code, {}) - eval_code_ex(code, UserDict(a=1)) self.assertRaises(TypeError, eval_code_ex, code, []) self.assertRaises(TypeError, eval_code_ex, code, 1) # CRASHES eval_code_ex(code, NULL) From bb888ff90f59f68121fee54d6ba9553f7eeab685 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Tue, 9 Jul 2024 14:54:43 +0800 Subject: [PATCH 29/49] updated style --- Python/bltinmodule.c | 6 ++++-- Python/bytecodes.c | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 166f5e3856ba96..62fc516be1a6d2 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1001,7 +1001,8 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, if (r == 0) { r = PyObject_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); } - } else { + } + else { PyErr_Format(PyExc_TypeError, "globals must be a mapping or None, not %.100s", Py_TYPE(globals)->tp_name); return NULL; @@ -1112,7 +1113,8 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, if (r == 0) { r = PyObject_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); } - } else { + } + else { PyErr_Format(PyExc_TypeError, "globals must be a mapping or None, not %.100s", Py_TYPE(globals)->tp_name); goto error; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index cfe524577db7d6..a4f2032b05166f 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1486,7 +1486,8 @@ dummy_func( int err; if (PyDict_Check(globals)) { err = PyDict_Pop(globals, name, NULL); - } else { + } + else { err = PyMapping_DelItem(globals, name); } // Can't use ERROR_IF here. From db01f51f5363e037e134bce3d1a797c1ee5c48bb Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Tue, 9 Jul 2024 15:26:14 +0800 Subject: [PATCH 30/49] regen headers --- Python/executor_cases.c.h | 3 ++- Python/generated_cases.c.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index ec61b57825f042..28783cae971429 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1534,7 +1534,8 @@ int err; if (PyDict_Check(globals)) { err = PyDict_Pop(globals, name, NULL); - } else { + } + else { err = PyMapping_DelItem(globals, name); } // Can't use ERROR_IF here. diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index f3b7302f493bad..a36d73ac7735b0 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2782,7 +2782,8 @@ int err; if (PyDict_Check(globals)) { err = PyDict_Pop(globals, name, NULL); - } else { + } + else { err = PyMapping_DelItem(globals, name); } // Can't use ERROR_IF here. From f0cd4e8f5b20d759b5d8cfe0e4cea1591bc31023 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Tue, 9 Jul 2024 16:03:43 +0800 Subject: [PATCH 31/49] removed comments that are no longer applicable --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index e25f8e8afdc99a..2746e7ca67b113 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -608,7 +608,7 @@ PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals) if (locals == NULL) { locals = globals; } - PyObject *builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); // borrowed ref + PyObject *builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); if (builtins == NULL) { return NULL; } @@ -1897,7 +1897,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, if (defaults == NULL) { return NULL; } - PyObject *builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); // borrowed ref + PyObject *builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); if (builtins == NULL) { Py_DECREF(defaults); return NULL; From d7d7d04aeaee3b92e13b106f81ebe7f77432e7b5 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Thu, 11 Jul 2024 11:49:09 +0800 Subject: [PATCH 32/49] allow a mapping to be object.__dict__ and by extension module.__dict__ and function.__globals__; added macros for unified dict/mapping API --- Doc/library/stdtypes.rst | 14 +- Doc/reference/datamodel.rst | 111 ++++++++------- Include/pymacro.h | 48 +++++++ Lib/test/test_funcattrs.py | 7 +- Objects/clinic/funcobject.c.h | 8 +- Objects/dictobject.c | 13 +- Objects/frameobject.c | 11 +- Objects/funcobject.c | 30 ++-- Objects/moduleobject.c | 249 ++++++++++++++++++++++------------ Objects/object.c | 23 ++-- Python/bltinmodule.c | 42 +++--- Python/bytecodes.c | 28 ++-- Python/ceval.c | 8 +- Python/pythonrun.c | 35 ++--- 14 files changed, 369 insertions(+), 258 deletions(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index d3f7cfb01d3c21..5a8293288b7610 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5293,12 +5293,12 @@ foo`` does not require a module object named *foo* to exist, rather it requires an (external) *definition* for a module named *foo* somewhere.) A special attribute of every module is :attr:`~object.__dict__`. This is the -dictionary containing the module's symbol table. Modifying this dictionary will -actually change the module's symbol table, but direct assignment to the +:term:`mapping`` containing the module's symbol table. Modifying this mapping +will actually change the module's symbol table, but direct assignment to the :attr:`~object.__dict__` attribute is not possible (you can write ``m.__dict__['a'] = 1``, which defines ``m.a`` to be ``1``, but you can't write -``m.__dict__ = {}``). Modifying :attr:`~object.__dict__` directly is -not recommended. +``m.__dict__ = {}``). Modifying :attr:`~object.__dict__` directly is not +recommended. Modules built into the interpreter are written like this: ````. If loaded from a file, they are written as ``` that holds the function's + - A reference to the :term:`mapping` that holds the function's :ref:`global variables ` -- the global namespace of the module in which the function was defined. @@ -868,15 +868,15 @@ the :ref:`import system ` as invoked either by the :keyword:`import` statement, or by calling functions such as :func:`importlib.import_module` and built-in :func:`__import__`. A module object has a namespace implemented by a -:class:`dictionary ` object (this is the dictionary referenced by the +:term:`mapping` object (this is the mapping referenced by the :attr:`~function.__globals__` attribute of functions defined in the module). Attribute references are -translated to lookups in this dictionary, e.g., ``m.x`` is equivalent to +translated to lookups in this mapping, e.g., ``m.x`` is equivalent to ``m.__dict__["x"]``. A module object does not contain the code object used to initialize the module (since it isn't needed once the initialization is done). -Attribute assignment updates the module's namespace dictionary, e.g., +Attribute assignment updates the module's namespace mapping, e.g., ``m.x = 1`` is equivalent to ``m.__dict__["x"] = 1``. .. index:: @@ -913,22 +913,27 @@ Predefined (writable) attributes: .. index:: single: __dict__ (module attribute) Special read-only attribute: :attr:`~object.__dict__` is the module's -namespace as a dictionary object. +namespace as a mapping object. .. impl-detail:: - Because of the way CPython clears module dictionaries, the module - dictionary will be cleared when the module falls out of scope even if the - dictionary still has live references. To avoid this, copy the dictionary - or keep the module around while using its dictionary directly. + Because of the way CPython clears module's namespace mapping, the module + namespace mapping will be cleared when the module falls out of scope even if + the namespace mapping still has live references. To avoid this, copy the + namespace mapping or keep the module around while using its namespace + mapping directly. + +.. versionchanged:: 3.14 + + The :attr:`~object.__dict__` attribute may now be a mapping. Custom classes -------------- Custom class types are typically created by class definitions (see section -:ref:`class`). A class has a namespace implemented by a dictionary object. -Class attribute references are translated to lookups in this dictionary, e.g., +:ref:`class`). A class has a namespace implemented by a mapping object. +Class attribute references are translated to lookups in this mapping, e.g., ``C.x`` is translated to ``C.__dict__["x"]`` (although there are a number of hooks which allow for other means of locating attributes). When the attribute name is not found there, the attribute search continues in the base classes. @@ -958,8 +963,8 @@ retrieved from a class may differ from those actually contained in its .. index:: triple: class; attribute; assignment -Class attribute assignments update the class's dictionary, never the dictionary -of a base class. +Class attribute assignments update the class's attribute mapping, never the +attribute mapping of a base class. .. index:: pair: class object; call @@ -985,7 +990,11 @@ Special attributes: The name of the module in which the class was defined. :attr:`~object.__dict__` - The dictionary containing the class's namespace. + The mapping containing the class's namespace. + + .. versionchanged:: 3.14 + + The :attr:`~object.__dict__` attribute may now be a mapping. :attr:`~class.__bases__` A tuple containing the base classes, in the order of @@ -1023,7 +1032,7 @@ Class instances pair: class instance; attribute A class instance is created by calling a class object (see above). A class -instance has a namespace implemented as a dictionary which is the first place +instance has a namespace implemented as a mapping which is the first place in which attribute references are searched. When an attribute is not found there, and the instance's class has an attribute by that name, the search continues with the class attributes. If a class attribute is found that is a @@ -1038,10 +1047,10 @@ the lookup. .. index:: triple: class instance; attribute; assignment -Attribute assignments and deletions update the instance's dictionary, never a -class's dictionary. If the class has a :meth:`~object.__setattr__` or -:meth:`~object.__delattr__` method, this is called instead of updating the instance -dictionary directly. +Attribute assignments and deletions update the instance's attribute mapping, +never a class's attribute mapping. If the class has a +:meth:`~object.__setattr__` or :meth:`~object.__delattr__` method, this is +called instead of updating the instance attribute mapping directly. .. index:: pair: object; numeric @@ -1055,9 +1064,13 @@ methods with certain special names. See section :ref:`specialnames`. single: __dict__ (instance attribute) single: __class__ (instance attribute) -Special attributes: :attr:`~object.__dict__` is the attribute dictionary; +Special attributes: :attr:`~object.__dict__` is the attribute mapping; :attr:`~instance.__class__` is the instance's class. +.. versionchanged:: 3.14 + + The :attr:`~object.__dict__` attribute may now be a mapping. + I/O objects (also known as file objects) ---------------------------------------- @@ -1109,7 +1122,7 @@ Code objects Code objects represent *byte-compiled* executable Python code, or :term:`bytecode`. The difference between a code object and a function object is that the function -object contains an explicit reference to the function's globals (the module in +object contains an explicit reference to the function's mapping (the module in which it was defined), while a code object contains no context; also the default argument values are stored in the function object, not in the code object (because they represent values calculated at run-time). Unlike function @@ -1360,7 +1373,7 @@ Special read-only attributes :ref:`global variables ` .. versionchanged:: 3.14 - May now be a mapping passed to :func:`exec` or :func:`eval`. + The :attr:`~frame.f_globals` attribute may now be a mapping. * - .. attribute:: frame.f_builtins - The mapping used by the frame to look up @@ -1961,8 +1974,8 @@ access (use of, assignment to, or deletion of ``x.name``) for class instances. :meth:`__getattr__` and :meth:`__setattr__`.) This is done both for efficiency reasons and because otherwise :meth:`__getattr__` would have no way to access other attributes of the instance. Note that at least for instance variables, - you can take total control by not inserting any values in the instance attribute - dictionary (but instead inserting them in another object). See the + you can take total control by not inserting any values in the instance + attribute mapping (but instead inserting them in another object). See the :meth:`__getattribute__` method below for a way to actually get total control over attribute access. @@ -1995,8 +2008,9 @@ access (use of, assignment to, or deletion of ``x.name``) for class instances. .. method:: object.__setattr__(self, name, value) Called when an attribute assignment is attempted. This is called instead of - the normal mechanism (i.e. store the value in the instance dictionary). - *name* is the attribute name, *value* is the value to be assigned to it. + the normal mechanism (i.e. store the value in the instance attribute + mapping). *name* is the attribute name, *value* is the value to be assigned + to it. If :meth:`__setattr__` wants to assign to an instance attribute, it should call the base class method with the same name, for example, @@ -2069,7 +2083,7 @@ a module object to a subclass of :class:`types.ModuleType`. For example:: Defining module ``__getattr__`` and setting module ``__class__`` only affect lookups made using the attribute access syntax -- directly accessing the module globals (whether by code within the module, or via a reference - to the module's globals dictionary) is unaffected. + to the module's globals mapping) is unaffected. .. versionchanged:: 3.5 ``__class__`` module attribute is now writable. @@ -2090,10 +2104,10 @@ Implementing Descriptors The following methods only apply when an instance of the class containing the method (a so-called *descriptor* class) appears in an *owner* class (the -descriptor must be in either the owner's class dictionary or in the class -dictionary for one of its parents). In the examples below, "the attribute" -refers to the attribute whose name is the key of the property in the owner -class' :attr:`~object.__dict__`. +descriptor must be in either the owner's class attribute mapping or in the +class attribute mapping for one of its parents). In the examples below, "the +attribute" refers to the attribute whose name is the key of the property in the +owner class' :attr:`~object.__dict__`. .. method:: object.__get__(self, instance, owner=None) @@ -2152,9 +2166,9 @@ protocol: :meth:`~object.__get__`, :meth:`~object.__set__`, and those methods are defined for an object, it is said to be a descriptor. The default behavior for attribute access is to get, set, or delete the -attribute from an object's dictionary. For instance, ``a.x`` has a lookup chain -starting with ``a.__dict__['x']``, then ``type(a).__dict__['x']``, and -continuing through the base classes of ``type(a)`` excluding metaclasses. +attribute from an object's attribute mapping. For instance, ``a.x`` has a +lookup chain starting with ``a.__dict__['x']``, then ``type(a).__dict__['x']``, +and continuing through the base classes of ``type(a)`` excluding metaclasses. However, if the looked-up value is an object defining one of the descriptor methods, then Python may override the default behavior and invoke the descriptor @@ -2217,17 +2231,16 @@ Super Binding For instance bindings, the precedence of descriptor invocation depends on which descriptor methods are defined. A descriptor can define any combination of :meth:`~object.__get__`, :meth:`~object.__set__` and -:meth:`~object.__delete__`. If it does not -define :meth:`!__get__`, then accessing the attribute will return the descriptor -object itself unless there is a value in the object's instance dictionary. If -the descriptor defines :meth:`!__set__` and/or :meth:`!__delete__`, it is a data -descriptor; if it defines neither, it is a non-data descriptor. Normally, data -descriptors define both :meth:`!__get__` and :meth:`!__set__`, while non-data -descriptors have just the :meth:`!__get__` method. Data descriptors with -:meth:`!__get__` and :meth:`!__set__` (and/or :meth:`!__delete__`) defined -always override a redefinition in an -instance dictionary. In contrast, non-data descriptors can be overridden by -instances. +:meth:`~object.__delete__`. If it does not define :meth:`!__get__`, then +accessing the attribute will return the descriptor object itself unless there +is a value in the object's instance attribute mapping. If the descriptor +defines :meth:`!__set__` and/or :meth:`!__delete__`, it is a data descriptor; +if it defines neither, it is a non-data descriptor. Normally, data descriptors +define both :meth:`!__get__` and :meth:`!__set__`, while non-data descriptors +have just the :meth:`!__get__` method. Data descriptors with :meth:`!__get__` +and :meth:`!__set__` (and/or :meth:`!__delete__`) defined always override a +redefinition in an instance attribute mapping. In contrast, non-data +descriptors can be overridden by instances. Python methods (including those decorated with :func:`@staticmethod ` and :func:`@classmethod `) are @@ -2920,8 +2933,8 @@ through the object's keys; for sequences, it should iterate through the values. .. method:: object.__missing__(self, key) - Called by :class:`dict`\ .\ :meth:`__getitem__` to implement ``self[key]`` for dict subclasses - when key is not in the dictionary. + Called by :class:`dict`\ .\ :meth:`__getitem__` to implement ``self[key]`` + for dict subclasses when key is not in the dictionary. .. method:: object.__iter__(self) @@ -3266,8 +3279,8 @@ Special method lookup For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object's type, not in the object's instance -dictionary. That behaviour is the reason why the following code raises an -exception:: +attribute mapping. That behaviour is the reason why the following code raises +an exception:: >>> class C: ... pass diff --git a/Include/pymacro.h b/Include/pymacro.h index a7945ef84a46fc..2fc885ec2cc3d9 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -190,4 +190,52 @@ // "comparison of unsigned expression in '< 0' is always false". #define _Py_IS_TYPE_SIGNED(type) ((type)(-1) <= 0) +/* Test if dict_or_mapping is a dict, call dict_func if it is, or call + * mapping_func if not. If test_mapping is TEST_MAPPING, also test if + * dict_or_mapping is a mapping before calling mapping_func. test_mapping + * should be set to NOTEST_MAPPING otherwise. + */ +#define _Py_IF_DICT_OR_MAPPING(dict_or_mapping, result, dict_func, \ + mapping_func, test_mapping, ...) _Py_IF_DICT_OR_MAPPING_##test_mapping( \ + dict_or_mapping, result, dict_func, mapping_func, ##__VA_ARGS__) + +#define _Py_IF_DICT_OR_MAPPING_TEST_MAPPING(dict_or_mapping, result, \ + dict_func, mapping_func, ...) \ + if (PyDict_Check(dict_or_mapping)) { \ + result = dict_func(dict_or_mapping, ##__VA_ARGS__); \ + } \ + else if (PyMapping_Check(dict_or_mapping)) { \ + result = mapping_func(dict_or_mapping, ##__VA_ARGS__); \ + } + +#define _Py_IF_DICT_OR_MAPPING_NOTEST_MAPPING(dict_or_mapping, result, \ + dict_func, mapping_func, ...) \ + if (PyDict_Check(dict_or_mapping)) { \ + result = dict_func(dict_or_mapping, ##__VA_ARGS__); \ + } \ + else { \ + result = mapping_func(dict_or_mapping, ##__VA_ARGS__); \ + } + +// Unified API for dict and mapping +#define _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict_or_mapping, obj, ref, result, \ + test_mapping) _Py_IF_DICT_OR_MAPPING(dict_or_mapping, result, \ + PyDict_GetItemRef, PyMapping_GetOptionalItem, test_mapping, obj, ref) + +#define _Py_IF_DICT_OR_MAPPING_SETITEM(dict_or_mapping, obj, ref, result, \ + test_mapping) _Py_IF_DICT_OR_MAPPING(dict_or_mapping, result, \ + PyDict_SetItem, PyObject_SetItem, test_mapping, obj, ref) + +#define _Py_IF_DICT_OR_MAPPING_DELITEM(dict_or_mapping, obj, result, \ + test_mapping) _Py_IF_DICT_OR_MAPPING(dict_or_mapping, result, \ + PyDict_DelItem, PyMapping_DelItem, test_mapping, obj) + +#define _Py_IF_DICT_OR_MAPPING_CONTAINS(dict_or_mapping, obj, result, \ + test_mapping) _Py_IF_DICT_OR_MAPPING(dict_or_mapping, result, \ + PyDict_Contains, PyMapping_HasKeyWithError, test_mapping, obj) + +#define _Py_IF_DICT_OR_MAPPING_KEYS(dict_or_mapping, result, test_mapping) \ + _Py_IF_DICT_OR_MAPPING(dict_or_mapping, result, PyDict_Keys, \ + PyMapping_Keys, test_mapping) + #endif /* Py_PYMACRO_H */ diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index b3fc5ad42e7fde..56cd85fbe4f5ef 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -351,9 +351,6 @@ def test_unset_attr(self): class FunctionDictsTest(FuncAttrsTest): def test_setting_dict_to_invalid(self): self.cannot_set_attr(self.b, '__dict__', None, TypeError) - from collections import UserDict - d = UserDict({'known_attr': 7}) - self.cannot_set_attr(self.fi.a.__func__, '__dict__', d, TypeError) def test_setting_dict_to_valid(self): d = {'known_attr': 7} @@ -370,6 +367,10 @@ def test_setting_dict_to_valid(self): # ... and again, on all the different method's names self.assertEqual(self.fi.a.__func__.known_attr, 7) self.assertEqual(self.fi.a.known_attr, 7) + from collections import UserDict + d = UserDict({'known_attr': 7}) + self.b.__dict__ = d + self.assertEqual(self.b.known_attr, 7) def test_delete___dict__(self): try: diff --git a/Objects/clinic/funcobject.c.h b/Objects/clinic/funcobject.c.h index 8f20bda26438cf..6bafa606dc43f0 100644 --- a/Objects/clinic/funcobject.c.h +++ b/Objects/clinic/funcobject.c.h @@ -18,7 +18,7 @@ PyDoc_STRVAR(func_new__doc__, " code\n" " a code object\n" " globals\n" -" the globals dictionary\n" +" the globals mapping\n" " name\n" " a string that overrides the name from the code object\n" " argdefs\n" @@ -82,10 +82,6 @@ func_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) goto exit; } code = (PyCodeObject *)fastargs[0]; - if (!PyDict_Check(fastargs[1])) { - _PyArg_BadArgument("function", "argument 'globals'", "dict", fastargs[1]); - goto exit; - } globals = fastargs[1]; if (!noptargs) { goto skip_optional_pos; @@ -115,4 +111,4 @@ func_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=10947342188f38a9 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=ea683473928f1d5d input=a9049054013a1b77]*/ diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 2b11a01595b0bc..0c7dad6c6fa497 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -7258,10 +7258,15 @@ _PyObjectDict_SetItem(PyTypeObject *tp, PyObject *obj, PyObject **dictptr, return -1; } - Py_BEGIN_CRITICAL_SECTION(dict); - res = _PyDict_SetItem_LockHeld((PyDictObject *)dict, key, value); - ASSERT_CONSISTENT(dict); - Py_END_CRITICAL_SECTION(); + if (PyDict_Check(dict)) { + Py_BEGIN_CRITICAL_SECTION(dict); + res = _PyDict_SetItem_LockHeld((PyDictObject *)dict, key, value); + ASSERT_CONSISTENT(dict); + Py_END_CRITICAL_SECTION(); + } + else { + res = PyObject_SetItem(dict, key, value); + } return res; } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index b95275378ea19a..e7fa9be1123983 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -2107,14 +2107,9 @@ _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) { PyObject *maybe_builtins; int has_builtins; - if (PyDict_Check(globals)) { - has_builtins = PyDict_GetItemRef(globals, - &_Py_ID(__builtins__), &maybe_builtins); - } - else { - has_builtins = PyMapping_GetOptionalItem(globals, - &_Py_ID(__builtins__), &maybe_builtins); - } + _Py_IF_DICT_OR_MAPPING_GETITEMREF(globals, &_Py_ID(__builtins__), + &maybe_builtins, has_builtins, + NOTEST_MAPPING) if (has_builtins < 0) { return NULL; } diff --git a/Objects/funcobject.c b/Objects/funcobject.c index e8b5d13e535195..4cbe991d0c9c5f 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -104,14 +104,8 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr) { PyObject *module; int r; - if (PyDict_Check(constr->fc_globals)) { - r = PyDict_GetItemRef(constr->fc_globals, &_Py_ID(__name__), - &module); - } - else { - r = PyMapping_GetOptionalItem(constr->fc_globals, &_Py_ID(__name__), - &module); - } + _Py_IF_DICT_OR_MAPPING_GETITEMREF(constr->fc_globals, &_Py_ID(__name__), + &module, r, NOTEST_MAPPING) if (r < 0) { return NULL; } @@ -150,7 +144,6 @@ PyObject * PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname) { assert(globals != NULL); - assert(PyDict_Check(globals)); Py_INCREF(globals); PyThreadState *tstate = _PyThreadState_GET(); @@ -183,7 +176,13 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname // __module__: Use globals['__name__'] if it exists, or NULL. PyObject *module; PyObject *builtins = NULL; - if (PyDict_GetItemRef(globals, &_Py_ID(__name__), &module) < 0) { + int r; + _Py_IF_DICT_OR_MAPPING_GETITEMREF(globals, &_Py_ID(__name__), &module, r, + TEST_MAPPING) + else { + goto error; + } + if (r < 0) { goto error; } @@ -930,8 +929,8 @@ class function "PyFunctionObject *" "&PyFunction_Type" function.__new__ as func_new code: object(type="PyCodeObject *", subclass_of="&PyCode_Type") a code object - globals: object(subclass_of="&PyDict_Type") - the globals dictionary + globals: object + the globals mapping name: object = None a string that overrides the name from the code object argdefs as defaults: object = None @@ -948,11 +947,16 @@ static PyObject * func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals, PyObject *name, PyObject *defaults, PyObject *closure, PyObject *kwdefaults) -/*[clinic end generated code: output=de72f4c22ac57144 input=20c9c9f04ad2d3f2]*/ +/*[clinic end generated code: output=de72f4c22ac57144 input=d6fcf687f69bd689]*/ { PyFunctionObject *newfunc; Py_ssize_t nclosure; + if (!PyMapping_Check(globals)) { + PyErr_SetString(PyExc_TypeError, + "arg 2 (globals) must be a mapping"); + return NULL; + } if (name != Py_None && !PyUnicode_Check(name)) { PyErr_SetString(PyExc_TypeError, "arg 3 (name) must be None or string"); diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 73ad9711b6b0fc..1e083fc37ca976 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -62,16 +62,20 @@ module_init_dict(PyModuleObject *mod, PyObject *md_dict, if (doc == NULL) doc = Py_None; - if (PyDict_SetItem(md_dict, &_Py_ID(__name__), name) != 0) - return -1; - if (PyDict_SetItem(md_dict, &_Py_ID(__doc__), doc) != 0) - return -1; - if (PyDict_SetItem(md_dict, &_Py_ID(__package__), Py_None) != 0) - return -1; - if (PyDict_SetItem(md_dict, &_Py_ID(__loader__), Py_None) != 0) - return -1; - if (PyDict_SetItem(md_dict, &_Py_ID(__spec__), Py_None) != 0) - return -1; + int r; + +#define _Py_MODULE_INIT_SETATTR(name, value) \ + _Py_IF_DICT_OR_MAPPING_SETITEM(md_dict, &_Py_ID(name), value, r, \ + NOTEST_MAPPING) \ + if (r != 0) { \ + return -1; \ + } + + _Py_MODULE_INIT_SETATTR(__name__, name) + _Py_MODULE_INIT_SETATTR(__doc__, doc) + _Py_MODULE_INIT_SETATTR(__package__, Py_None) + _Py_MODULE_INIT_SETATTR(__loader__, Py_None) + _Py_MODULE_INIT_SETATTR(__spec__, Py_None) if (PyUnicode_CheckExact(name)) { Py_XSETREF(mod->md_name, Py_NewRef(name)); } @@ -566,11 +570,14 @@ PyModule_GetNameObject(PyObject *mod) return NULL; } PyObject *dict = ((PyModuleObject *)mod)->md_dict; // borrowed reference - if (dict == NULL || !PyDict_Check(dict)) { + if (dict == NULL || !PyMapping_Check(dict)) { goto error; } PyObject *name; - if (PyDict_GetItemRef(dict, &_Py_ID(__name__), &name) <= 0) { + int r; + _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__name__), &name, r, + NOTEST_MAPPING) + if (r <= 0) { // error or not found goto error; } @@ -611,7 +618,10 @@ PyModule_GetFilenameObject(PyObject *mod) goto error; } PyObject *fileobj; - if (PyDict_GetItemRef(dict, &_Py_ID(__file__), &fileobj) <= 0) { + int r; + _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__file__), &fileobj, r, + NOTEST_MAPPING) + if (r <= 0) { // error or not found goto error; } @@ -672,57 +682,79 @@ _PyModule_Clear(PyObject *m) void _PyModule_ClearDict(PyObject *d) { - /* To make the execution order of destructors for global - objects a bit more predictable, we first zap all objects - whose name starts with a single underscore, before we clear - the entire dictionary. We zap them by replacing them with - None, rather than deleting them from the dictionary, to - avoid rehashing the dictionary (to some extent). */ + /* To make the execution order of destructors for global objects a bit + more predictable, we first zap all objects whose name starts with a + single underscore, before we clear the entire mapping. We zap them + by replacing them with None, rather than deleting them from the mapping, + to avoid rehashing the mapping (to some extent). */ Py_ssize_t pos; PyObject *key, *value; int verbose = _Py_GetConfig()->verbose; - /* First, clear only names starting with a single underscore */ - pos = 0; - while (PyDict_Next(d, &pos, &key, &value)) { - if (value != Py_None && PyUnicode_Check(key)) { - if (PyUnicode_READ_CHAR(key, 0) == '_' && - PyUnicode_READ_CHAR(key, 1) != '_') { - if (verbose > 1) { - const char *s = PyUnicode_AsUTF8(key); - if (s != NULL) - PySys_WriteStderr("# clear[1] %s\n", s); - else - PyErr_Clear(); - } - if (PyDict_SetItem(d, key, Py_None) != 0) { - PyErr_FormatUnraisable("Exception ignored on clearing module dict"); - } - } - } +#define _Py_MODULE_CLEARDICT_ITER_DICT(action_block) \ + pos = 0; \ + while (PyDict_Next(d, &pos, &key, &value)) { \ + action_block \ + } + +#define _Py_MODULE_CLEARDICT_ITER_MAPPING(action_block) \ + for (pos = 0; pos < size; pos++) { \ + PyObject *key_value = PyList_GET_ITEM(items, pos); \ + key = PyTuple_GET_ITEM(key_value, 0); \ + value = PyTuple_GET_ITEM(key_value, 1); \ + action_block \ + } + +#define _Py_MODULE_CLEARDICT_BY(filter, phase) \ + if (value != Py_None && PyUnicode_Check(key)) { \ + if (filter) { \ + if (verbose > 1) { \ + const char *s = PyUnicode_AsUTF8(key); \ + if (s != NULL) \ + PySys_WriteStderr("# clear[" phase "] %s\n", s); \ + else \ + PyErr_Clear(); \ + } \ + int r; \ + _Py_IF_DICT_OR_MAPPING_SETITEM(d, key, Py_None, r, \ + NOTEST_MAPPING) \ + if (r != 0) { \ + PyErr_FormatUnraisable( \ + "Exception ignored on clearing module dict"); \ + } \ + } \ + } + + if (PyDict_Check(d)) { + /* First, clear only names starting with a single underscore */ + _Py_MODULE_CLEARDICT_ITER_DICT( + _Py_MODULE_CLEARDICT_BY( + PyUnicode_READ_CHAR(key, 0) == '_' && + PyUnicode_READ_CHAR(key, 1) != '_', "1")) + + /* Next, clear all names except for __builtins__ */ + _Py_MODULE_CLEARDICT_ITER_DICT( + _Py_MODULE_CLEARDICT_BY( + PyUnicode_READ_CHAR(key, 0) != '_' && + !_PyUnicode_EqualToASCIIString(key, "__builtins__"), "2")) } + else { + PyObject *items = PyMapping_Items(d); + Py_ssize_t size = PyList_Size(items); - /* Next, clear all names except for __builtins__ */ - pos = 0; - while (PyDict_Next(d, &pos, &key, &value)) { - if (value != Py_None && PyUnicode_Check(key)) { - if (PyUnicode_READ_CHAR(key, 0) != '_' || - !_PyUnicode_EqualToASCIIString(key, "__builtins__")) - { - if (verbose > 1) { - const char *s = PyUnicode_AsUTF8(key); - if (s != NULL) - PySys_WriteStderr("# clear[2] %s\n", s); - else - PyErr_Clear(); - } - if (PyDict_SetItem(d, key, Py_None) != 0) { - PyErr_FormatUnraisable("Exception ignored on clearing module dict"); - } - } - } + /* First, clear only names starting with a single underscore */ + _Py_MODULE_CLEARDICT_ITER_MAPPING( + _Py_MODULE_CLEARDICT_BY( + PyUnicode_READ_CHAR(key, 0) == '_' && + PyUnicode_READ_CHAR(key, 1) != '_', "1")) + + /* Next, clear all names except for __builtins__ */ + _Py_MODULE_CLEARDICT_ITER_MAPPING( + _Py_MODULE_CLEARDICT_BY( + PyUnicode_READ_CHAR(key, 0) != '_' && + !_PyUnicode_EqualToASCIIString(key, "__builtins__"), "2")) } /* Note: we leave __builtins__ in place, so that destructors @@ -940,7 +972,10 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) PyErr_Clear(); } assert(m->md_dict != NULL); - if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__getattr__), &getattr) < 0) { + int r; + _Py_IF_DICT_OR_MAPPING_GETITEMREF(m->md_dict, &_Py_ID(__getattr__), + &getattr, r, NOTEST_MAPPING) + if (r < 0) { return NULL; } if (getattr) { @@ -958,7 +993,9 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) if (suppress == 1) { return NULL; } - if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__name__), &mod_name) < 0) { + _Py_IF_DICT_OR_MAPPING_GETITEMREF(m->md_dict, &_Py_ID(__name__), &mod_name, + r, NOTEST_MAPPING) + if (r < 0) { return NULL; } if (!mod_name || !PyUnicode_Check(mod_name)) { @@ -968,7 +1005,9 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) return NULL; } PyObject *spec; - if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__spec__), &spec) < 0) { + _Py_IF_DICT_OR_MAPPING_GETITEMREF(m->md_dict, &_Py_ID(__spec__), &spec, r, + NOTEST_MAPPING) + if (r < 0) { Py_DECREF(mod_name); return NULL; } @@ -1109,18 +1148,22 @@ module_dir(PyObject *self, PyObject *args) PyObject *dict = PyObject_GetAttr(self, &_Py_ID(__dict__)); if (dict != NULL) { - if (PyDict_Check(dict)) { - PyObject *dirfunc = PyDict_GetItemWithError(dict, &_Py_ID(__dir__)); - if (dirfunc) { - result = _PyObject_CallNoArgs(dirfunc); + PyObject *dirfunc; + int r; + _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__dir__), &dirfunc, r, + TEST_MAPPING) + else { + PyErr_Format(PyExc_TypeError, ".__dict__ is not a mapping"); + } + if (!PyErr_Occurred()) { + if (r == 0) { + _Py_IF_DICT_OR_MAPPING_KEYS(dict, result, NOTEST_MAPPING) } - else if (!PyErr_Occurred()) { - result = PyDict_Keys(dict); + else { + result = _PyObject_CallNoArgs(dirfunc); + Py_DECREF(dirfunc); } } - else { - PyErr_Format(PyExc_TypeError, ".__dict__ is not a dictionary"); - } } Py_XDECREF(dict); @@ -1140,8 +1183,8 @@ module_get_dict(PyModuleObject *m) if (dict == NULL) { return NULL; } - if (!PyDict_Check(dict)) { - PyErr_Format(PyExc_TypeError, ".__dict__ is not a dictionary"); + if (!PyMapping_Check(dict)) { + PyErr_Format(PyExc_TypeError, ".__dict__ is not a mapping"); Py_DECREF(dict); return NULL; } @@ -1157,9 +1200,14 @@ module_get_annotate(PyModuleObject *m, void *Py_UNUSED(ignored)) } PyObject *annotate; - if (PyDict_GetItemRef(dict, &_Py_ID(__annotate__), &annotate) == 0) { + int r; + _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__annotate__), &annotate, + r, NOTEST_MAPPING) + if (r == 0) { annotate = Py_None; - if (PyDict_SetItem(dict, &_Py_ID(__annotate__), annotate) == -1) { + _Py_IF_DICT_OR_MAPPING_SETITEM(dict, &_Py_ID(__annotate__), annotate, + r, NOTEST_MAPPING) + if (r == -1) { Py_CLEAR(annotate); } } @@ -1185,14 +1233,22 @@ module_set_annotate(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignored) return -1; } - if (PyDict_SetItem(dict, &_Py_ID(__annotate__), value) == -1) { + int r; + _Py_IF_DICT_OR_MAPPING_SETITEM(dict, &_Py_ID(__annotate__), value, r, + NOTEST_MAPPING) + if (r == -1) { Py_DECREF(dict); return -1; } if (!Py_IsNone(value)) { - if (PyDict_Pop(dict, &_Py_ID(__annotations__), NULL) == -1) { - Py_DECREF(dict); - return -1; + _Py_IF_DICT_OR_MAPPING_DELITEM(dict, &_Py_ID(__annotations__), r, + NOTEST_MAPPING) + if (r == -1) { + if (!PyErr_ExceptionMatches(PyExc_KeyError)) { + Py_DECREF(dict); + return -1; + } + PyErr_Clear(); } } Py_DECREF(dict); @@ -1208,9 +1264,15 @@ module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored)) } PyObject *annotations; - if (PyDict_GetItemRef(dict, &_Py_ID(__annotations__), &annotations) == 0) { + int r; + _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__annotations__), + &annotations, r, NOTEST_MAPPING) + if (r == 0) { PyObject *annotate; - int annotate_result = PyDict_GetItemRef(dict, &_Py_ID(__annotate__), &annotate); + int annotate_result; + _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__annotate__), + &annotate, annotate_result, + NOTEST_MAPPING) if (annotate_result < 0) { Py_DECREF(dict); return NULL; @@ -1224,7 +1286,8 @@ module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored)) return NULL; } if (!PyDict_Check(annotations)) { - PyErr_Format(PyExc_TypeError, "__annotate__ returned non-dict of type '%.100s'", + PyErr_Format(PyExc_TypeError, + "__annotate__ returned non-dict of type '%.100s'", Py_TYPE(annotations)->tp_name); Py_DECREF(annotate); Py_DECREF(annotations); @@ -1237,8 +1300,9 @@ module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored)) } Py_XDECREF(annotate); if (annotations) { - int result = PyDict_SetItem( - dict, &_Py_ID(__annotations__), annotations); + int result; + _Py_IF_DICT_OR_MAPPING_SETITEM(dict, &_Py_ID(__annotations__), + annotations, result, NOTEST_MAPPING) if (result) { Py_CLEAR(annotations); } @@ -1259,21 +1323,32 @@ module_set_annotations(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignor if (value != NULL) { /* set */ - ret = PyDict_SetItem(dict, &_Py_ID(__annotations__), value); + _Py_IF_DICT_OR_MAPPING_SETITEM(dict, &_Py_ID(__annotations__), value, + ret, NOTEST_MAPPING) } else { /* delete */ - ret = PyDict_Pop(dict, &_Py_ID(__annotations__), NULL); - if (ret == 0) { + _Py_IF_DICT_OR_MAPPING_DELITEM(dict, &_Py_ID(__annotations__), ret, + NOTEST_MAPPING) + if (ret == -1 && PyErr_ExceptionMatches(PyExc_KeyError)) { PyErr_SetObject(PyExc_AttributeError, &_Py_ID(__annotations__)); - ret = -1; } else if (ret > 0) { ret = 0; } } - if (ret == 0 && PyDict_Pop(dict, &_Py_ID(__annotate__), NULL) < 0) { - ret = -1; + if (ret == 0) { + int r; + _Py_IF_DICT_OR_MAPPING_DELITEM(dict, &_Py_ID(__annotate__), r, + NOTEST_MAPPING) + if (r == -1) { + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + } + else { + ret = -1; + } + } } Py_DECREF(dict); diff --git a/Objects/object.c b/Objects/object.c index c4622359bb1035..27e4e48cd83f90 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1498,7 +1498,10 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) } if (dict != NULL) { Py_INCREF(dict); - if (PyDict_GetItemRef(dict, name, method) != 0) { + int r; + _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, name, method, r, + NOTEST_MAPPING) + if (r != 0) { // found or error Py_DECREF(dict); Py_XDECREF(descr); @@ -1604,7 +1607,8 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, } if (dict != NULL) { Py_INCREF(dict); - int rc = PyDict_GetItemRef(dict, name, &res); + int rc; + _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, name, &res, rc, NOTEST_MAPPING) Py_DECREF(dict); if (res != NULL) { goto done; @@ -1729,10 +1733,13 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, } else { Py_INCREF(dict); - if (value == NULL) - res = PyDict_DelItem(dict, name); - else - res = PyDict_SetItem(dict, name, value); + if (value == NULL) { + _Py_IF_DICT_OR_MAPPING_DELITEM(dict, name, res, NOTEST_MAPPING) + } + else { + _Py_IF_DICT_OR_MAPPING_SETITEM(dict, name, value, res, + NOTEST_MAPPING) + } Py_DECREF(dict); } error_check: @@ -1776,9 +1783,9 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context) PyErr_SetString(PyExc_TypeError, "cannot delete __dict__"); return -1; } - if (!PyDict_Check(value)) { + if (!PyMapping_Check(value)) { PyErr_Format(PyExc_TypeError, - "__dict__ must be set to a dictionary, " + "__dict__ must be set to a mapping, " "not a '%.200s'", Py_TYPE(value)->tp_name); return -1; } diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 62fc516be1a6d2..4eb6f88ed376bb 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -990,18 +990,8 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, } int r; - if (PyDict_Check(globals)) { - r = PyDict_Contains(globals, &_Py_ID(__builtins__)); - if (r == 0) { - r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); - } - } - else if (PyMapping_Check(globals)) { - r = PyMapping_HasKeyWithError(globals, &_Py_ID(__builtins__)); - if (r == 0) { - r = PyObject_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); - } - } + _Py_IF_DICT_OR_MAPPING_CONTAINS(globals, &_Py_ID(__builtins__), r, + TEST_MAPPING) else { PyErr_Format(PyExc_TypeError, "globals must be a mapping or None, not %.100s", Py_TYPE(globals)->tp_name); @@ -1010,6 +1000,13 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, if (r < 0) { goto error; } + if (r == 0) { + _Py_IF_DICT_OR_MAPPING_SETITEM(globals, &_Py_ID(__builtins__), + PyEval_GetBuiltins(), r, NOTEST_MAPPING) + if (r < 0) { + goto error; + } + } if (PyCode_Check(source)) { if (PySys_Audit("exec", "O", source) < 0) { @@ -1102,18 +1099,8 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, } int r; - if (PyDict_Check(globals)) { - r = PyDict_Contains(globals, &_Py_ID(__builtins__)); - if (r == 0) { - r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); - } - } - else if (PyMapping_Check(globals)) { - r = PyMapping_HasKeyWithError(globals, &_Py_ID(__builtins__)); - if (r == 0) { - r = PyObject_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins()); - } - } + _Py_IF_DICT_OR_MAPPING_CONTAINS(globals, &_Py_ID(__builtins__), r, + TEST_MAPPING) else { PyErr_Format(PyExc_TypeError, "globals must be a mapping or None, not %.100s", Py_TYPE(globals)->tp_name); @@ -1122,6 +1109,13 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, if (r < 0) { goto error; } + if (r == 0) { + _Py_IF_DICT_OR_MAPPING_SETITEM(globals, &_Py_ID(__builtins__), + PyEval_GetBuiltins(), r, NOTEST_MAPPING) + if (r < 0) { + goto error; + } + } if (closure == Py_None) { closure = NULL; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index a4f2032b05166f..3cffe4d99a8573 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1468,14 +1468,9 @@ dummy_func( PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err; - if (PyDict_Check(globals)) { - err = PyDict_SetItem(globals, name, - PyStackRef_AsPyObjectBorrow(v)); - } - else { - err = PyObject_SetItem(globals, name, - PyStackRef_AsPyObjectBorrow(v)); - } + _Py_IF_DICT_OR_MAPPING_SETITEM(globals, name, + PyStackRef_AsPyObjectBorrow(v), err, + NOTEST_MAPPING) DECREF_INPUTS(); ERROR_IF(err, error); } @@ -1484,12 +1479,7 @@ dummy_func( PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err; - if (PyDict_Check(globals)) { - err = PyDict_Pop(globals, name, NULL); - } - else { - err = PyMapping_DelItem(globals, name); - } + _Py_IF_DICT_OR_MAPPING_DELITEM(globals, name, err, NOTEST_MAPPING) // Can't use ERROR_IF here. if (err < 0) { if (_PyErr_Occurred(tstate) && @@ -1568,12 +1558,10 @@ dummy_func( ERROR_NO_POP(); } if (v_o == NULL) { - if (PyDict_Check(globals)) { - if (PyDict_GetItemRef(globals, name, &v_o) < 0) { - ERROR_NO_POP(); - } - } - else if (PyMapping_GetOptionalItem(globals, name, &v_o) < 0) { + int r; + _Py_IF_DICT_OR_MAPPING_GETITEMREF(globals, name, &v_o, r, + NOTEST_MAPPING) + if (r < 0) { ERROR_NO_POP(); } if (v_o == NULL) { diff --git a/Python/ceval.c b/Python/ceval.c index 2746e7ca67b113..dfb2aa479d7c5a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -206,12 +206,8 @@ maybe_lltrace_resume_frame(_PyInterpreterFrame *frame, _PyInterpreterFrame *skip return 0; } int r; - if (PyDict_Check(globals)) { - r = PyDict_Contains(globals, &_Py_ID(__lltrace__)); - } - else { - r = PyMapping_HasKeyWithError(globals, &_Py_ID(__lltrace__)); - } + _Py_IF_DICT_OR_MAPPING_CONTAINS(globals, &_Py_ID(__lltrace__), r, + NOTEST_MAPPING) if (r < 0) { return -1; } diff --git a/Python/pythonrun.c b/Python/pythonrun.c index ca1b1d8488c49e..b355a13342e6df 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1277,35 +1277,22 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py if (!globals) { goto error; } - else if (PyDict_Check(globals)) { - has_builtins = PyDict_ContainsString(globals, "__builtins__"); - if (has_builtins < 0) { - return NULL; - } - if (!has_builtins) { - if (PyDict_SetItemString(globals, "__builtins__", - tstate->interp->builtins) < 0) { - return NULL; - } - } - } - else if (PyMapping_Check(globals)) { - has_builtins = PyMapping_HasKeyStringWithError(globals, "__builtins__"); - if (has_builtins < 0) { - return NULL; - } - if (!has_builtins) { - if (PyMapping_SetItemString(globals, "__builtins__", - tstate->interp->builtins) < 0) { - return NULL; - } - } - } + else _Py_IF_DICT_OR_MAPPING_CONTAINS(globals, &_Py_ID(__builtins__), + has_builtins, TEST_MAPPING) else { error: PyErr_SetString(PyExc_SystemError, "globals must be a mapping"); return NULL; } + if (!has_builtins) { + int r; + _Py_IF_DICT_OR_MAPPING_SETITEM(globals, &_Py_ID(__builtins__), + tstate->interp->builtins, r, + NOTEST_MAPPING) + if (r < 0) { + return NULL; + } + } v = PyEval_EvalCode((PyObject*)co, globals, locals); if (!v && _PyErr_Occurred(tstate) == PyExc_KeyboardInterrupt) { From cab9a525c4458e3f1dcc05690834cf9c3c8e1c38 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Thu, 11 Jul 2024 12:27:26 +0800 Subject: [PATCH 33/49] simplified macros for unified dict/mapping API --- Include/pymacro.h | 54 +++++++++++++++++---------------- Objects/frameobject.c | 5 ++-- Objects/funcobject.c | 14 ++++----- Objects/moduleobject.c | 68 +++++++++++++++++------------------------- Objects/object.c | 10 +++---- Python/bltinmodule.c | 38 ++++++++++++----------- Python/bytecodes.c | 10 +++---- Python/ceval.c | 3 +- Python/pythonrun.c | 18 +++++------ 9 files changed, 103 insertions(+), 117 deletions(-) diff --git a/Include/pymacro.h b/Include/pymacro.h index 2fc885ec2cc3d9..cdaaa850c29195 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -195,47 +195,51 @@ * dict_or_mapping is a mapping before calling mapping_func. test_mapping * should be set to NOTEST_MAPPING otherwise. */ -#define _Py_IF_DICT_OR_MAPPING(dict_or_mapping, result, dict_func, \ - mapping_func, test_mapping, ...) _Py_IF_DICT_OR_MAPPING_##test_mapping( \ - dict_or_mapping, result, dict_func, mapping_func, ##__VA_ARGS__) - -#define _Py_IF_DICT_OR_MAPPING_TEST_MAPPING(dict_or_mapping, result, \ +#define _Py_DICT_OR_MAPPING(dict_or_mapping, result, \ dict_func, mapping_func, ...) \ if (PyDict_Check(dict_or_mapping)) { \ result = dict_func(dict_or_mapping, ##__VA_ARGS__); \ } \ - else if (PyMapping_Check(dict_or_mapping)) { \ + else { \ result = mapping_func(dict_or_mapping, ##__VA_ARGS__); \ } -#define _Py_IF_DICT_OR_MAPPING_NOTEST_MAPPING(dict_or_mapping, result, \ - dict_func, mapping_func, ...) \ +#define _Py_DICT_OR_MAPPING_ELSE(dict_or_mapping, result, \ + dict_func, mapping_func, else_block, ...) \ if (PyDict_Check(dict_or_mapping)) { \ result = dict_func(dict_or_mapping, ##__VA_ARGS__); \ } \ - else { \ + else if (PyMapping_Check(dict_or_mapping)) { \ result = mapping_func(dict_or_mapping, ##__VA_ARGS__); \ - } + } \ + else else_block // Unified API for dict and mapping -#define _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict_or_mapping, obj, ref, result, \ - test_mapping) _Py_IF_DICT_OR_MAPPING(dict_or_mapping, result, \ - PyDict_GetItemRef, PyMapping_GetOptionalItem, test_mapping, obj, ref) +#define _Py_DICT_OR_MAPPING_GETITEMREF(dict_or_mapping, obj, ref, result) \ + _Py_DICT_OR_MAPPING(dict_or_mapping, result, PyDict_GetItemRef, \ + PyMapping_GetOptionalItem, obj, ref) + +#define _Py_DICT_OR_MAPPING_GETITEMREF_ELSE(dict_or_mapping, obj, ref, \ + result, else_block) _Py_DICT_OR_MAPPING_ELSE(dict_or_mapping, result, \ + PyDict_GetItemRef, PyMapping_GetOptionalItem, else_block, obj, ref) + +#define _Py_DICT_OR_MAPPING_SETITEM(dict_or_mapping, obj, ref, result) \ + _Py_DICT_OR_MAPPING(dict_or_mapping, result, PyDict_SetItem, \ + PyObject_SetItem, obj, ref) -#define _Py_IF_DICT_OR_MAPPING_SETITEM(dict_or_mapping, obj, ref, result, \ - test_mapping) _Py_IF_DICT_OR_MAPPING(dict_or_mapping, result, \ - PyDict_SetItem, PyObject_SetItem, test_mapping, obj, ref) +#define _Py_DICT_OR_MAPPING_DELITEM(dict_or_mapping, obj, result) \ + _Py_DICT_OR_MAPPING(dict_or_mapping, result, PyDict_DelItem, \ + PyMapping_DelItem, obj) -#define _Py_IF_DICT_OR_MAPPING_DELITEM(dict_or_mapping, obj, result, \ - test_mapping) _Py_IF_DICT_OR_MAPPING(dict_or_mapping, result, \ - PyDict_DelItem, PyMapping_DelItem, test_mapping, obj) +#define _Py_DICT_OR_MAPPING_CONTAINS(dict_or_mapping, obj, result) \ + _Py_DICT_OR_MAPPING(dict_or_mapping, result, PyDict_Contains, \ + PyMapping_HasKeyWithError, obj) -#define _Py_IF_DICT_OR_MAPPING_CONTAINS(dict_or_mapping, obj, result, \ - test_mapping) _Py_IF_DICT_OR_MAPPING(dict_or_mapping, result, \ - PyDict_Contains, PyMapping_HasKeyWithError, test_mapping, obj) +#define _Py_DICT_OR_MAPPING_CONTAINS_ELSE(dict_or_mapping, obj, result, \ + else_block) _Py_DICT_OR_MAPPING_ELSE(dict_or_mapping, result, \ + PyDict_Contains, PyMapping_HasKeyWithError, else_block, obj) -#define _Py_IF_DICT_OR_MAPPING_KEYS(dict_or_mapping, result, test_mapping) \ - _Py_IF_DICT_OR_MAPPING(dict_or_mapping, result, PyDict_Keys, \ - PyMapping_Keys, test_mapping) +#define _Py_DICT_OR_MAPPING_KEYS(dict_or_mapping, result) \ + _Py_DICT_OR_MAPPING(dict_or_mapping, result, PyDict_Keys, PyMapping_Keys) #endif /* Py_PYMACRO_H */ diff --git a/Objects/frameobject.c b/Objects/frameobject.c index e7fa9be1123983..f4d93d8a570d81 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -2107,9 +2107,8 @@ _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) { PyObject *maybe_builtins; int has_builtins; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(globals, &_Py_ID(__builtins__), - &maybe_builtins, has_builtins, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_GETITEMREF(globals, &_Py_ID(__builtins__), + &maybe_builtins, has_builtins) if (has_builtins < 0) { return NULL; } diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 4cbe991d0c9c5f..d4ac13c3b0f5e2 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -104,8 +104,8 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr) { PyObject *module; int r; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(constr->fc_globals, &_Py_ID(__name__), - &module, r, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_GETITEMREF(constr->fc_globals, &_Py_ID(__name__), + &module, r) if (r < 0) { return NULL; } @@ -177,11 +177,11 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname PyObject *module; PyObject *builtins = NULL; int r; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(globals, &_Py_ID(__name__), &module, r, - TEST_MAPPING) - else { - goto error; - } + _Py_DICT_OR_MAPPING_GETITEMREF_ELSE(globals, &_Py_ID(__name__), &module, r, + { + goto error; + } + ) if (r < 0) { goto error; } diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 1e083fc37ca976..778c2cdbd87ce0 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -65,8 +65,7 @@ module_init_dict(PyModuleObject *mod, PyObject *md_dict, int r; #define _Py_MODULE_INIT_SETATTR(name, value) \ - _Py_IF_DICT_OR_MAPPING_SETITEM(md_dict, &_Py_ID(name), value, r, \ - NOTEST_MAPPING) \ + _Py_DICT_OR_MAPPING_SETITEM(md_dict, &_Py_ID(name), value, r) \ if (r != 0) { \ return -1; \ } @@ -575,8 +574,7 @@ PyModule_GetNameObject(PyObject *mod) } PyObject *name; int r; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__name__), &name, r, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__name__), &name, r) if (r <= 0) { // error or not found goto error; @@ -619,8 +617,7 @@ PyModule_GetFilenameObject(PyObject *mod) } PyObject *fileobj; int r; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__file__), &fileobj, r, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__file__), &fileobj, r) if (r <= 0) { // error or not found goto error; @@ -718,8 +715,7 @@ _PyModule_ClearDict(PyObject *d) PyErr_Clear(); \ } \ int r; \ - _Py_IF_DICT_OR_MAPPING_SETITEM(d, key, Py_None, r, \ - NOTEST_MAPPING) \ + _Py_DICT_OR_MAPPING_SETITEM(d, key, Py_None, r) \ if (r != 0) { \ PyErr_FormatUnraisable( \ "Exception ignored on clearing module dict"); \ @@ -973,8 +969,7 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) } assert(m->md_dict != NULL); int r; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(m->md_dict, &_Py_ID(__getattr__), - &getattr, r, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_GETITEMREF(m->md_dict, &_Py_ID(__getattr__), &getattr, r) if (r < 0) { return NULL; } @@ -993,8 +988,7 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) if (suppress == 1) { return NULL; } - _Py_IF_DICT_OR_MAPPING_GETITEMREF(m->md_dict, &_Py_ID(__name__), &mod_name, - r, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_GETITEMREF(m->md_dict, &_Py_ID(__name__), &mod_name, r) if (r < 0) { return NULL; } @@ -1005,8 +999,7 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress) return NULL; } PyObject *spec; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(m->md_dict, &_Py_ID(__spec__), &spec, r, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_GETITEMREF(m->md_dict, &_Py_ID(__spec__), &spec, r) if (r < 0) { Py_DECREF(mod_name); return NULL; @@ -1150,14 +1143,15 @@ module_dir(PyObject *self, PyObject *args) if (dict != NULL) { PyObject *dirfunc; int r; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__dir__), &dirfunc, r, - TEST_MAPPING) - else { - PyErr_Format(PyExc_TypeError, ".__dict__ is not a mapping"); - } + _Py_DICT_OR_MAPPING_GETITEMREF_ELSE(dict, &_Py_ID(__dir__), &dirfunc, r, + { + PyErr_Format(PyExc_TypeError, + ".__dict__ is not a mapping"); + } + ) if (!PyErr_Occurred()) { if (r == 0) { - _Py_IF_DICT_OR_MAPPING_KEYS(dict, result, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_KEYS(dict, result) } else { result = _PyObject_CallNoArgs(dirfunc); @@ -1201,12 +1195,10 @@ module_get_annotate(PyModuleObject *m, void *Py_UNUSED(ignored)) PyObject *annotate; int r; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__annotate__), &annotate, - r, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__annotate__), &annotate, r) if (r == 0) { annotate = Py_None; - _Py_IF_DICT_OR_MAPPING_SETITEM(dict, &_Py_ID(__annotate__), annotate, - r, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_SETITEM(dict, &_Py_ID(__annotate__), annotate, r) if (r == -1) { Py_CLEAR(annotate); } @@ -1234,15 +1226,13 @@ module_set_annotate(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignored) } int r; - _Py_IF_DICT_OR_MAPPING_SETITEM(dict, &_Py_ID(__annotate__), value, r, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_SETITEM(dict, &_Py_ID(__annotate__), value, r) if (r == -1) { Py_DECREF(dict); return -1; } if (!Py_IsNone(value)) { - _Py_IF_DICT_OR_MAPPING_DELITEM(dict, &_Py_ID(__annotations__), r, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_DELITEM(dict, &_Py_ID(__annotations__), r) if (r == -1) { if (!PyErr_ExceptionMatches(PyExc_KeyError)) { Py_DECREF(dict); @@ -1265,14 +1255,13 @@ module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored)) PyObject *annotations; int r; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__annotations__), - &annotations, r, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__annotations__), + &annotations, r) if (r == 0) { PyObject *annotate; int annotate_result; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__annotate__), - &annotate, annotate_result, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_GETITEMREF(dict, &_Py_ID(__annotate__), + &annotate, annotate_result) if (annotate_result < 0) { Py_DECREF(dict); return NULL; @@ -1301,8 +1290,8 @@ module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored)) Py_XDECREF(annotate); if (annotations) { int result; - _Py_IF_DICT_OR_MAPPING_SETITEM(dict, &_Py_ID(__annotations__), - annotations, result, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_SETITEM(dict, &_Py_ID(__annotations__), + annotations, result) if (result) { Py_CLEAR(annotations); } @@ -1323,13 +1312,11 @@ module_set_annotations(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignor if (value != NULL) { /* set */ - _Py_IF_DICT_OR_MAPPING_SETITEM(dict, &_Py_ID(__annotations__), value, - ret, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_SETITEM(dict, &_Py_ID(__annotations__), value, ret) } else { /* delete */ - _Py_IF_DICT_OR_MAPPING_DELITEM(dict, &_Py_ID(__annotations__), ret, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_DELITEM(dict, &_Py_ID(__annotations__), ret) if (ret == -1 && PyErr_ExceptionMatches(PyExc_KeyError)) { PyErr_SetObject(PyExc_AttributeError, &_Py_ID(__annotations__)); } @@ -1339,8 +1326,7 @@ module_set_annotations(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignor } if (ret == 0) { int r; - _Py_IF_DICT_OR_MAPPING_DELITEM(dict, &_Py_ID(__annotate__), r, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_DELITEM(dict, &_Py_ID(__annotate__), r) if (r == -1) { if (PyErr_ExceptionMatches(PyExc_KeyError)) { PyErr_Clear(); diff --git a/Objects/object.c b/Objects/object.c index 27e4e48cd83f90..1c34a724b422ed 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1499,8 +1499,7 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) if (dict != NULL) { Py_INCREF(dict); int r; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, name, method, r, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_GETITEMREF(dict, name, method, r) if (r != 0) { // found or error Py_DECREF(dict); @@ -1608,7 +1607,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, if (dict != NULL) { Py_INCREF(dict); int rc; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(dict, name, &res, rc, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_GETITEMREF(dict, name, &res, rc) Py_DECREF(dict); if (res != NULL) { goto done; @@ -1734,11 +1733,10 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name, else { Py_INCREF(dict); if (value == NULL) { - _Py_IF_DICT_OR_MAPPING_DELITEM(dict, name, res, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_DELITEM(dict, name, res) } else { - _Py_IF_DICT_OR_MAPPING_SETITEM(dict, name, value, res, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_SETITEM(dict, name, value, res) } Py_DECREF(dict); } diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 4eb6f88ed376bb..56f12ed209c56b 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -990,19 +990,20 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals, } int r; - _Py_IF_DICT_OR_MAPPING_CONTAINS(globals, &_Py_ID(__builtins__), r, - TEST_MAPPING) - else { - PyErr_Format(PyExc_TypeError, "globals must be a mapping or None, not %.100s", - Py_TYPE(globals)->tp_name); - return NULL; - } + _Py_DICT_OR_MAPPING_CONTAINS_ELSE(globals, &_Py_ID(__builtins__), r, + { + PyErr_Format(PyExc_TypeError, + "globals must be a mapping or None, not %.100s", + Py_TYPE(globals)->tp_name); + return NULL; + } + ) if (r < 0) { goto error; } if (r == 0) { - _Py_IF_DICT_OR_MAPPING_SETITEM(globals, &_Py_ID(__builtins__), - PyEval_GetBuiltins(), r, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_SETITEM(globals, &_Py_ID(__builtins__), + PyEval_GetBuiltins(), r) if (r < 0) { goto error; } @@ -1099,19 +1100,20 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals, } int r; - _Py_IF_DICT_OR_MAPPING_CONTAINS(globals, &_Py_ID(__builtins__), r, - TEST_MAPPING) - else { - PyErr_Format(PyExc_TypeError, "globals must be a mapping or None, not %.100s", - Py_TYPE(globals)->tp_name); - goto error; - } + _Py_DICT_OR_MAPPING_CONTAINS_ELSE(globals, &_Py_ID(__builtins__), r, + { + PyErr_Format(PyExc_TypeError, + "globals must be a mapping or None, not %.100s", + Py_TYPE(globals)->tp_name); + goto error; + } + ) if (r < 0) { goto error; } if (r == 0) { - _Py_IF_DICT_OR_MAPPING_SETITEM(globals, &_Py_ID(__builtins__), - PyEval_GetBuiltins(), r, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_SETITEM(globals, &_Py_ID(__builtins__), + PyEval_GetBuiltins(), r) if (r < 0) { goto error; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 3cffe4d99a8573..329d058b37d87a 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1468,9 +1468,8 @@ dummy_func( PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err; - _Py_IF_DICT_OR_MAPPING_SETITEM(globals, name, - PyStackRef_AsPyObjectBorrow(v), err, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_SETITEM(globals, name, + PyStackRef_AsPyObjectBorrow(v), err) DECREF_INPUTS(); ERROR_IF(err, error); } @@ -1479,7 +1478,7 @@ dummy_func( PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err; - _Py_IF_DICT_OR_MAPPING_DELITEM(globals, name, err, NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_DELITEM(globals, name, err) // Can't use ERROR_IF here. if (err < 0) { if (_PyErr_Occurred(tstate) && @@ -1559,8 +1558,7 @@ dummy_func( } if (v_o == NULL) { int r; - _Py_IF_DICT_OR_MAPPING_GETITEMREF(globals, name, &v_o, r, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_GETITEMREF(globals, name, &v_o, r) if (r < 0) { ERROR_NO_POP(); } diff --git a/Python/ceval.c b/Python/ceval.c index dfb2aa479d7c5a..448c0d973dbd6e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -206,8 +206,7 @@ maybe_lltrace_resume_frame(_PyInterpreterFrame *frame, _PyInterpreterFrame *skip return 0; } int r; - _Py_IF_DICT_OR_MAPPING_CONTAINS(globals, &_Py_ID(__lltrace__), r, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_CONTAINS(globals, &_Py_ID(__lltrace__), r) if (r < 0) { return -1; } diff --git a/Python/pythonrun.c b/Python/pythonrun.c index b355a13342e6df..4aba4ef27e3a68 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1277,18 +1277,18 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py if (!globals) { goto error; } - else _Py_IF_DICT_OR_MAPPING_CONTAINS(globals, &_Py_ID(__builtins__), - has_builtins, TEST_MAPPING) - else { + else _Py_DICT_OR_MAPPING_CONTAINS_ELSE(globals, &_Py_ID(__builtins__), + has_builtins, + { error: - PyErr_SetString(PyExc_SystemError, "globals must be a mapping"); - return NULL; - } + PyErr_SetString(PyExc_SystemError, "globals must be a mapping"); + return NULL; + } + ) if (!has_builtins) { int r; - _Py_IF_DICT_OR_MAPPING_SETITEM(globals, &_Py_ID(__builtins__), - tstate->interp->builtins, r, - NOTEST_MAPPING) + _Py_DICT_OR_MAPPING_SETITEM(globals, &_Py_ID(__builtins__), + tstate->interp->builtins, r) if (r < 0) { return NULL; } From f8c62c6ea76d9801c26cf8e60a85d3a51783eabd Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Thu, 11 Jul 2024 12:33:17 +0800 Subject: [PATCH 34/49] corrected comments --- Include/pymacro.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Include/pymacro.h b/Include/pymacro.h index cdaaa850c29195..7563300a2a67bf 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -190,11 +190,8 @@ // "comparison of unsigned expression in '< 0' is always false". #define _Py_IS_TYPE_SIGNED(type) ((type)(-1) <= 0) -/* Test if dict_or_mapping is a dict, call dict_func if it is, or call - * mapping_func if not. If test_mapping is TEST_MAPPING, also test if - * dict_or_mapping is a mapping before calling mapping_func. test_mapping - * should be set to NOTEST_MAPPING otherwise. - */ +// Test if dict_or_mapping is a dict, and call dict_func if it is, or call +// mapping_func if not. Store the returning value in result. #define _Py_DICT_OR_MAPPING(dict_or_mapping, result, \ dict_func, mapping_func, ...) \ if (PyDict_Check(dict_or_mapping)) { \ @@ -204,6 +201,9 @@ result = mapping_func(dict_or_mapping, ##__VA_ARGS__); \ } +// Test if dict_or_mapping is a dict, and call dict_func if it is, or call +// mapping_func if dict_or_mapping is a mapping; otherwise execute else_block. +// Store the returning value in result. #define _Py_DICT_OR_MAPPING_ELSE(dict_or_mapping, result, \ dict_func, mapping_func, else_block, ...) \ if (PyDict_Check(dict_or_mapping)) { \ From aa2d9b4ea78cc7ac80927f3d83948da9b1cc4f7e Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Thu, 11 Jul 2024 13:14:03 +0800 Subject: [PATCH 35/49] regen headers --- .../pycore_global_objects_fini_generated.h | 1 - Include/internal/pycore_global_strings.h | 1 - .../internal/pycore_runtime_init_generated.h | 1 - .../internal/pycore_unicodeobject_generated.h | 4 --- Python/executor_cases.c.h | 17 +++--------- Python/generated_cases.c.h | 26 +++++-------------- 6 files changed, 9 insertions(+), 41 deletions(-) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index c0840f9eb7eca2..49fae83434da4d 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -662,7 +662,6 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__len__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__length_hint__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__lltrace__)); - _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__loader__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__lshift__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__lt__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__main__)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 51735a8a726e11..d44a6b00e89642 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -151,7 +151,6 @@ struct _Py_global_strings { STRUCT_FOR_ID(__len__) STRUCT_FOR_ID(__length_hint__) STRUCT_FOR_ID(__lltrace__) - STRUCT_FOR_ID(__loader__) STRUCT_FOR_ID(__lshift__) STRUCT_FOR_ID(__lt__) STRUCT_FOR_ID(__main__) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index c5be67c6d80b9d..0ed61a2b1e3353 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -660,7 +660,6 @@ extern "C" { INIT_ID(__len__), \ INIT_ID(__length_hint__), \ INIT_ID(__lltrace__), \ - INIT_ID(__loader__), \ INIT_ID(__lshift__), \ INIT_ID(__lt__), \ INIT_ID(__main__), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 0e0ad6518771e9..7dad8d4fd7bab5 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -404,10 +404,6 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); - string = &_Py_ID(__loader__); - _PyUnicode_InternStatic(interp, &string); - assert(_PyUnicode_CheckConsistency(string, 1)); - assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(__lshift__); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 28783cae971429..27144f58895127 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1512,14 +1512,8 @@ PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err; - if (PyDict_Check(globals)) { - err = PyDict_SetItem(globals, name, - PyStackRef_AsPyObjectBorrow(v)); - } - else { - err = PyObject_SetItem(globals, name, - PyStackRef_AsPyObjectBorrow(v)); - } + _Py_DICT_OR_MAPPING_SETITEM(globals, name, + PyStackRef_AsPyObjectBorrow(v), err) PyStackRef_CLOSE(v); if (err) JUMP_TO_ERROR(); stack_pointer += -1; @@ -1532,12 +1526,7 @@ PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err; - if (PyDict_Check(globals)) { - err = PyDict_Pop(globals, name, NULL); - } - else { - err = PyMapping_DelItem(globals, name); - } + _Py_DICT_OR_MAPPING_DELITEM(globals, name, err) // Can't use ERROR_IF here. if (err < 0) { if (_PyErr_Occurred(tstate) && diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index a36d73ac7735b0..ba0c723d00c869 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2780,12 +2780,7 @@ PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err; - if (PyDict_Check(globals)) { - err = PyDict_Pop(globals, name, NULL); - } - else { - err = PyMapping_DelItem(globals, name); - } + _Py_DICT_OR_MAPPING_DELITEM(globals, name, err) // Can't use ERROR_IF here. if (err < 0) { if (_PyErr_Occurred(tstate) && @@ -4993,12 +4988,9 @@ goto error; } if (v_o == NULL) { - if (PyDict_Check(globals)) { - if (PyDict_GetItemRef(globals, name, &v_o) < 0) { - goto error; - } - } - else if (PyMapping_GetOptionalItem(globals, name, &v_o) < 0) { + int r; + _Py_DICT_OR_MAPPING_GETITEMREF(globals, name, &v_o, r) + if (r < 0) { goto error; } if (v_o == NULL) { @@ -6198,14 +6190,8 @@ PyObject *globals = GLOBALS(); PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err; - if (PyDict_Check(globals)) { - err = PyDict_SetItem(globals, name, - PyStackRef_AsPyObjectBorrow(v)); - } - else { - err = PyObject_SetItem(globals, name, - PyStackRef_AsPyObjectBorrow(v)); - } + _Py_DICT_OR_MAPPING_SETITEM(globals, name, + PyStackRef_AsPyObjectBorrow(v), err) PyStackRef_CLOSE(v); if (err) goto pop_1_error; stack_pointer += -1; From 362e0ac6117d47308e9daaac24b8e5d93dc6ebf6 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Thu, 11 Jul 2024 13:41:10 +0800 Subject: [PATCH 36/49] fixed headers --- .../internal/pycore_global_objects_fini_generated.h | 1 + Include/internal/pycore_global_strings.h | 1 + Include/internal/pycore_runtime_init_generated.h | 1 + Include/internal/pycore_unicodeobject_generated.h | 4 ++++ Objects/moduleobject.c | 12 ++++++------ Python/pythonrun.c | 2 +- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 49fae83434da4d..c0840f9eb7eca2 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -662,6 +662,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__len__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__length_hint__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__lltrace__)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__loader__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__lshift__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__lt__)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__main__)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index d44a6b00e89642..51735a8a726e11 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -151,6 +151,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(__len__) STRUCT_FOR_ID(__length_hint__) STRUCT_FOR_ID(__lltrace__) + STRUCT_FOR_ID(__loader__) STRUCT_FOR_ID(__lshift__) STRUCT_FOR_ID(__lt__) STRUCT_FOR_ID(__main__) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 0ed61a2b1e3353..c5be67c6d80b9d 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -660,6 +660,7 @@ extern "C" { INIT_ID(__len__), \ INIT_ID(__length_hint__), \ INIT_ID(__lltrace__), \ + INIT_ID(__loader__), \ INIT_ID(__lshift__), \ INIT_ID(__lt__), \ INIT_ID(__main__), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 7dad8d4fd7bab5..0e0ad6518771e9 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -404,6 +404,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(__loader__); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(__lshift__); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 778c2cdbd87ce0..a5dd09d0b6df31 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -65,16 +65,16 @@ module_init_dict(PyModuleObject *mod, PyObject *md_dict, int r; #define _Py_MODULE_INIT_SETATTR(name, value) \ - _Py_DICT_OR_MAPPING_SETITEM(md_dict, &_Py_ID(name), value, r) \ + _Py_DICT_OR_MAPPING_SETITEM(md_dict, name, value, r) \ if (r != 0) { \ return -1; \ } - _Py_MODULE_INIT_SETATTR(__name__, name) - _Py_MODULE_INIT_SETATTR(__doc__, doc) - _Py_MODULE_INIT_SETATTR(__package__, Py_None) - _Py_MODULE_INIT_SETATTR(__loader__, Py_None) - _Py_MODULE_INIT_SETATTR(__spec__, Py_None) + _Py_MODULE_INIT_SETATTR(&_Py_ID(__name__), name) + _Py_MODULE_INIT_SETATTR(&_Py_ID(__doc__), doc) + _Py_MODULE_INIT_SETATTR(&_Py_ID(__package__), Py_None) + _Py_MODULE_INIT_SETATTR(&_Py_ID(__loader__), Py_None) + _Py_MODULE_INIT_SETATTR(&_Py_ID(__spec__), Py_None) if (PyUnicode_CheckExact(name)) { Py_XSETREF(mod->md_name, Py_NewRef(name)); } diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 4aba4ef27e3a68..ed8e4b86630c1c 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -387,7 +387,7 @@ set_main_loader(PyObject *d, PyObject *filename, const char *loader_name) return -1; } - if (PyDict_SetItemString(d, "__loader__", loader) < 0) { + if (PyDict_SetItem(d, &_Py_ID(__loader__), loader) < 0) { Py_DECREF(loader); return -1; } From bf7be59e549ef45b53db54baa57df0c5cb36d47b Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Thu, 11 Jul 2024 13:43:09 +0800 Subject: [PATCH 37/49] simplified macro --- Objects/moduleobject.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index a5dd09d0b6df31..778c2cdbd87ce0 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -65,16 +65,16 @@ module_init_dict(PyModuleObject *mod, PyObject *md_dict, int r; #define _Py_MODULE_INIT_SETATTR(name, value) \ - _Py_DICT_OR_MAPPING_SETITEM(md_dict, name, value, r) \ + _Py_DICT_OR_MAPPING_SETITEM(md_dict, &_Py_ID(name), value, r) \ if (r != 0) { \ return -1; \ } - _Py_MODULE_INIT_SETATTR(&_Py_ID(__name__), name) - _Py_MODULE_INIT_SETATTR(&_Py_ID(__doc__), doc) - _Py_MODULE_INIT_SETATTR(&_Py_ID(__package__), Py_None) - _Py_MODULE_INIT_SETATTR(&_Py_ID(__loader__), Py_None) - _Py_MODULE_INIT_SETATTR(&_Py_ID(__spec__), Py_None) + _Py_MODULE_INIT_SETATTR(__name__, name) + _Py_MODULE_INIT_SETATTR(__doc__, doc) + _Py_MODULE_INIT_SETATTR(__package__, Py_None) + _Py_MODULE_INIT_SETATTR(__loader__, Py_None) + _Py_MODULE_INIT_SETATTR(__spec__, Py_None) if (PyUnicode_CheckExact(name)) { Py_XSETREF(mod->md_name, Py_NewRef(name)); } From 0bf8239a8bfac5e60590ee59934b38be6922d637 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Thu, 11 Jul 2024 13:53:04 +0800 Subject: [PATCH 38/49] fixed reference count --- Objects/moduleobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 778c2cdbd87ce0..3a04a6ff2a2fd0 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -1155,9 +1155,9 @@ module_dir(PyObject *self, PyObject *args) } else { result = _PyObject_CallNoArgs(dirfunc); - Py_DECREF(dirfunc); } } + Py_XDECREF(dirfunc); } Py_XDECREF(dict); From 278c2ca14359a383a5c3667e5bbb0aaca6a78913 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Thu, 11 Jul 2024 13:55:46 +0800 Subject: [PATCH 39/49] fixed typo in doc --- Doc/library/stdtypes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 5a8293288b7610..148d48104ba6ac 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -5293,7 +5293,7 @@ foo`` does not require a module object named *foo* to exist, rather it requires an (external) *definition* for a module named *foo* somewhere.) A special attribute of every module is :attr:`~object.__dict__`. This is the -:term:`mapping`` containing the module's symbol table. Modifying this mapping +:term:`mapping` containing the module's symbol table. Modifying this mapping will actually change the module's symbol table, but direct assignment to the :attr:`~object.__dict__` attribute is not possible (you can write ``m.__dict__['a'] = 1``, which defines ``m.a`` to be ``1``, but you can't write From d6735b961e3b6aaebefe0b3bc9000063ff2d02f3 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Thu, 11 Jul 2024 14:12:14 +0800 Subject: [PATCH 40/49] fixed reference count --- Objects/moduleobject.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 3a04a6ff2a2fd0..645c4eff7dc152 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -751,6 +751,7 @@ _PyModule_ClearDict(PyObject *d) _Py_MODULE_CLEARDICT_BY( PyUnicode_READ_CHAR(key, 0) != '_' && !_PyUnicode_EqualToASCIIString(key, "__builtins__"), "2")) + Py_DECREF(items); } /* Note: we leave __builtins__ in place, so that destructors @@ -1155,9 +1156,9 @@ module_dir(PyObject *self, PyObject *args) } else { result = _PyObject_CallNoArgs(dirfunc); + Py_DECREF(dirfunc); } } - Py_XDECREF(dirfunc); } Py_XDECREF(dict); From 18dad2bb4e36d83d9617e128b0d8e9ae16bd8910 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Thu, 11 Jul 2024 14:21:08 +0800 Subject: [PATCH 41/49] fixed possible uninitialized usage --- Objects/moduleobject.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 645c4eff7dc152..f10a78e66cecc5 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -1142,22 +1142,20 @@ module_dir(PyObject *self, PyObject *args) PyObject *dict = PyObject_GetAttr(self, &_Py_ID(__dict__)); if (dict != NULL) { - PyObject *dirfunc; - int r; + PyObject *dirfunc = NULL; + int r = -1; _Py_DICT_OR_MAPPING_GETITEMREF_ELSE(dict, &_Py_ID(__dir__), &dirfunc, r, { PyErr_Format(PyExc_TypeError, ".__dict__ is not a mapping"); } ) - if (!PyErr_Occurred()) { - if (r == 0) { - _Py_DICT_OR_MAPPING_KEYS(dict, result) - } - else { - result = _PyObject_CallNoArgs(dirfunc); - Py_DECREF(dirfunc); - } + if (dirfunc) { + result = _PyObject_CallNoArgs(dirfunc); + Py_DECREF(dirfunc); + } + else if (r == 0) { + _Py_DICT_OR_MAPPING_KEYS(dict, result) } } From 35ada07b7279be41bb71e072d7d5832d4a9cb4f5 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Thu, 11 Jul 2024 18:05:24 +0800 Subject: [PATCH 42/49] fixed logical operation --- Objects/dictobject.c | 4 ++-- Objects/moduleobject.c | 11 +++++++---- Python/pythonrun.c | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 0c7dad6c6fa497..3f21e9f851eb76 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -7258,15 +7258,15 @@ _PyObjectDict_SetItem(PyTypeObject *tp, PyObject *obj, PyObject **dictptr, return -1; } + Py_BEGIN_CRITICAL_SECTION(dict); if (PyDict_Check(dict)) { - Py_BEGIN_CRITICAL_SECTION(dict); res = _PyDict_SetItem_LockHeld((PyDictObject *)dict, key, value); ASSERT_CONSISTENT(dict); - Py_END_CRITICAL_SECTION(); } else { res = PyObject_SetItem(dict, key, value); } + Py_END_CRITICAL_SECTION(); return res; } diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index f10a78e66cecc5..89e7622b6c985d 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -709,10 +709,12 @@ _PyModule_ClearDict(PyObject *d) if (filter) { \ if (verbose > 1) { \ const char *s = PyUnicode_AsUTF8(key); \ - if (s != NULL) \ + if (s != NULL) { \ PySys_WriteStderr("# clear[" phase "] %s\n", s); \ - else \ + } \ + else { \ PyErr_Clear(); \ + } \ } \ int r; \ _Py_DICT_OR_MAPPING_SETITEM(d, key, Py_None, r) \ @@ -733,7 +735,7 @@ _PyModule_ClearDict(PyObject *d) /* Next, clear all names except for __builtins__ */ _Py_MODULE_CLEARDICT_ITER_DICT( _Py_MODULE_CLEARDICT_BY( - PyUnicode_READ_CHAR(key, 0) != '_' && + PyUnicode_READ_CHAR(key, 0) != '_' || !_PyUnicode_EqualToASCIIString(key, "__builtins__"), "2")) } else { @@ -749,8 +751,9 @@ _PyModule_ClearDict(PyObject *d) /* Next, clear all names except for __builtins__ */ _Py_MODULE_CLEARDICT_ITER_MAPPING( _Py_MODULE_CLEARDICT_BY( - PyUnicode_READ_CHAR(key, 0) != '_' && + PyUnicode_READ_CHAR(key, 0) != '_' || !_PyUnicode_EqualToASCIIString(key, "__builtins__"), "2")) + Py_DECREF(items); } diff --git a/Python/pythonrun.c b/Python/pythonrun.c index ed8e4b86630c1c..bc13afe2fda45b 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1278,7 +1278,7 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py goto error; } else _Py_DICT_OR_MAPPING_CONTAINS_ELSE(globals, &_Py_ID(__builtins__), - has_builtins, + has_builtins, { error: PyErr_SetString(PyExc_SystemError, "globals must be a mapping"); From 2e6a88ae39421eb02bfbba092a2ccae2842cddce Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Thu, 11 Jul 2024 18:19:02 +0800 Subject: [PATCH 43/49] updated tests --- Lib/test/test_funcattrs.py | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index 56cd85fbe4f5ef..993b1d15d7a89b 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -353,24 +353,22 @@ def test_setting_dict_to_invalid(self): self.cannot_set_attr(self.b, '__dict__', None, TypeError) def test_setting_dict_to_valid(self): - d = {'known_attr': 7} - self.b.__dict__ = d - # Test assignment - self.assertIs(d, self.b.__dict__) - # ... and on all the different ways of referencing the method's func - self.F.a.__dict__ = d - self.assertIs(d, self.fi.a.__func__.__dict__) - self.assertIs(d, self.fi.a.__dict__) - # Test value - self.assertEqual(self.b.known_attr, 7) - self.assertEqual(self.b.__dict__['known_attr'], 7) - # ... and again, on all the different method's names - self.assertEqual(self.fi.a.__func__.known_attr, 7) - self.assertEqual(self.fi.a.known_attr, 7) from collections import UserDict - d = UserDict({'known_attr': 7}) - self.b.__dict__ = d - self.assertEqual(self.b.known_attr, 7) + for d in {'known_attr': 7}, UserDict({'known_attr': 7}): + with self.subTest(dict=d): + self.b.__dict__ = d + # Test assignment + self.assertIs(d, self.b.__dict__) + # ... and on all the different ways of referencing the method's func + self.F.a.__dict__ = d + self.assertIs(d, self.fi.a.__func__.__dict__) + self.assertIs(d, self.fi.a.__dict__) + # Test value + self.assertEqual(self.b.known_attr, 7) + self.assertEqual(self.b.__dict__['known_attr'], 7) + # ... and again, on all the different method's names + self.assertEqual(self.fi.a.__func__.known_attr, 7) + self.assertEqual(self.fi.a.known_attr, 7) def test_delete___dict__(self): try: From c29a10cd09ffa390cb1804fe9757c1ebde6cfe82 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 12 Jul 2024 09:11:08 +0800 Subject: [PATCH 44/49] added test for mapping __dict__ for module; DRYer code with better macros --- Lib/test/test_builtin.py | 6 +++++ Objects/moduleobject.c | 47 ++++++++++++++++------------------------ 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 5818e96d61f480..7b386dee47c5ff 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -572,6 +572,12 @@ class Foo(types.ModuleType): f = Foo("foo") self.assertRaises(TypeError, dir, f) + # dir(module_with_mapping__dict__) + class Foo(types.ModuleType): + __dict__ = collections.UserDict(x=7) + f = Foo("foo") + self.assertIn("x", dir(f)) + # dir(type) self.assertIn("strip", dir(str)) self.assertNotIn("__mro__", dir(str)) diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 89e7622b6c985d..2c56cd86aa9fa2 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -690,13 +690,14 @@ _PyModule_ClearDict(PyObject *d) int verbose = _Py_GetConfig()->verbose; -#define _Py_MODULE_CLEARDICT_ITER_DICT(action_block) \ +// define 3 macros to DRY up 4 similar blocks of code by type and filter +#define _Py_MODULE_CLEARDICT_FOR_DICT(action_block) \ pos = 0; \ while (PyDict_Next(d, &pos, &key, &value)) { \ action_block \ } -#define _Py_MODULE_CLEARDICT_ITER_MAPPING(action_block) \ +#define _Py_MODULE_CLEARDICT_FOR_MAPPING(action_block) \ for (pos = 0; pos < size; pos++) { \ PyObject *key_value = PyList_GET_ITEM(items, pos); \ key = PyTuple_GET_ITEM(key_value, 0); \ @@ -704,13 +705,13 @@ _PyModule_ClearDict(PyObject *d) action_block \ } -#define _Py_MODULE_CLEARDICT_BY(filter, phase) \ +#define _Py_MODULE_CLEARDICT_BY_FILTER(phase, filter) \ if (value != Py_None && PyUnicode_Check(key)) { \ if (filter) { \ if (verbose > 1) { \ const char *s = PyUnicode_AsUTF8(key); \ if (s != NULL) { \ - PySys_WriteStderr("# clear[" phase "] %s\n", s); \ + PySys_WriteStderr("# clear[" #phase "] %s\n", s); \ } \ else { \ PyErr_Clear(); \ @@ -725,35 +726,25 @@ _PyModule_ClearDict(PyObject *d) } \ } +#define _Py_MODULE_CLEARDICT_OF_TYPE(type) \ + /* First, clear only names starting with a single underscore */ \ + _Py_MODULE_CLEARDICT_FOR_##type( \ + _Py_MODULE_CLEARDICT_BY_FILTER(1, \ + PyUnicode_READ_CHAR(key, 0) == '_' && \ + PyUnicode_READ_CHAR(key, 1) != '_')) \ + /* Next, clear all names except for __builtins__ */ \ + _Py_MODULE_CLEARDICT_FOR_##type( \ + _Py_MODULE_CLEARDICT_BY_FILTER(2, \ + PyUnicode_READ_CHAR(key, 0) != '_' || \ + !_PyUnicode_EqualToASCIIString(key, "__builtins__"))) + if (PyDict_Check(d)) { - /* First, clear only names starting with a single underscore */ - _Py_MODULE_CLEARDICT_ITER_DICT( - _Py_MODULE_CLEARDICT_BY( - PyUnicode_READ_CHAR(key, 0) == '_' && - PyUnicode_READ_CHAR(key, 1) != '_', "1")) - - /* Next, clear all names except for __builtins__ */ - _Py_MODULE_CLEARDICT_ITER_DICT( - _Py_MODULE_CLEARDICT_BY( - PyUnicode_READ_CHAR(key, 0) != '_' || - !_PyUnicode_EqualToASCIIString(key, "__builtins__"), "2")) + _Py_MODULE_CLEARDICT_OF_TYPE(DICT) } else { PyObject *items = PyMapping_Items(d); Py_ssize_t size = PyList_Size(items); - - /* First, clear only names starting with a single underscore */ - _Py_MODULE_CLEARDICT_ITER_MAPPING( - _Py_MODULE_CLEARDICT_BY( - PyUnicode_READ_CHAR(key, 0) == '_' && - PyUnicode_READ_CHAR(key, 1) != '_', "1")) - - /* Next, clear all names except for __builtins__ */ - _Py_MODULE_CLEARDICT_ITER_MAPPING( - _Py_MODULE_CLEARDICT_BY( - PyUnicode_READ_CHAR(key, 0) != '_' || - !_PyUnicode_EqualToASCIIString(key, "__builtins__"), "2")) - + _Py_MODULE_CLEARDICT_OF_TYPE(MAPPING) Py_DECREF(items); } From 3513a29e191aee591920e1f57bf8dd48e4bb1601 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 12 Jul 2024 09:16:03 +0800 Subject: [PATCH 45/49] updated comment --- Objects/moduleobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 2c56cd86aa9fa2..52c1d30bf9283b 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -690,7 +690,7 @@ _PyModule_ClearDict(PyObject *d) int verbose = _Py_GetConfig()->verbose; -// define 3 macros to DRY up 4 similar blocks of code by type and filter +// Define 3 macros to DRY up 4 very similar blocks of code by type and filter #define _Py_MODULE_CLEARDICT_FOR_DICT(action_block) \ pos = 0; \ while (PyDict_Next(d, &pos, &key, &value)) { \ From 97b813dbdc309f1de02f2c4c8437d720b4fe0cee Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 12 Jul 2024 09:37:52 +0800 Subject: [PATCH 46/49] updated comment --- Objects/moduleobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 52c1d30bf9283b..ade3d6e4c90a68 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -690,7 +690,7 @@ _PyModule_ClearDict(PyObject *d) int verbose = _Py_GetConfig()->verbose; -// Define 3 macros to DRY up 4 very similar blocks of code by type and filter +/* Define macros to DRY up 4 very similar blocks of code by type and filter */ #define _Py_MODULE_CLEARDICT_FOR_DICT(action_block) \ pos = 0; \ while (PyDict_Next(d, &pos, &key, &value)) { \ From 6ea65b93ac794cf45ff280546e403f149d2c6ff5 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 12 Jul 2024 10:44:04 +0800 Subject: [PATCH 47/49] reformatted code; updated news --- Include/pymacro.h | 8 ++++---- .../2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Include/pymacro.h b/Include/pymacro.h index 7563300a2a67bf..648ddff26c349b 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -192,8 +192,8 @@ // Test if dict_or_mapping is a dict, and call dict_func if it is, or call // mapping_func if not. Store the returning value in result. -#define _Py_DICT_OR_MAPPING(dict_or_mapping, result, \ - dict_func, mapping_func, ...) \ +#define _Py_DICT_OR_MAPPING(dict_or_mapping, result, dict_func, mapping_func, \ + ...) \ if (PyDict_Check(dict_or_mapping)) { \ result = dict_func(dict_or_mapping, ##__VA_ARGS__); \ } \ @@ -204,8 +204,8 @@ // Test if dict_or_mapping is a dict, and call dict_func if it is, or call // mapping_func if dict_or_mapping is a mapping; otherwise execute else_block. // Store the returning value in result. -#define _Py_DICT_OR_MAPPING_ELSE(dict_or_mapping, result, \ - dict_func, mapping_func, else_block, ...) \ +#define _Py_DICT_OR_MAPPING_ELSE(dict_or_mapping, result, dict_func, \ + mapping_func, else_block, ...) \ if (PyDict_Check(dict_or_mapping)) { \ result = dict_func(dict_or_mapping, ##__VA_ARGS__); \ } \ diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst index 34da6bdcbfdefe..cf6656c3352b45 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst @@ -1,2 +1,2 @@ -Allow a mapping to be passed as the *globals* argument for the built-in functions :func:`exec` and :func:`eval`. +Allow a mapping to be the `__dict__` attribute of any object. This allows a mapping to be the global namespace of a module. It also allows a mapping to be passed as the *globals* argument for the built-in functions :func:`exec` and :func:`eval`. Patch by Ben Hsing From 8137cab4f84e0aca62f2083f6b390788f87d45e8 Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 12 Jul 2024 10:47:14 +0800 Subject: [PATCH 48/49] reformatted code for clarity --- Include/pymacro.h | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Include/pymacro.h b/Include/pymacro.h index 648ddff26c349b..dd588bdb6fa6f2 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -214,30 +214,32 @@ } \ else else_block -// Unified API for dict and mapping +// Unified dict/mapping API #define _Py_DICT_OR_MAPPING_GETITEMREF(dict_or_mapping, obj, ref, result) \ _Py_DICT_OR_MAPPING(dict_or_mapping, result, PyDict_GetItemRef, \ - PyMapping_GetOptionalItem, obj, ref) + PyMapping_GetOptionalItem, obj, ref) #define _Py_DICT_OR_MAPPING_GETITEMREF_ELSE(dict_or_mapping, obj, ref, \ - result, else_block) _Py_DICT_OR_MAPPING_ELSE(dict_or_mapping, result, \ - PyDict_GetItemRef, PyMapping_GetOptionalItem, else_block, obj, ref) + result, else_block) \ + _Py_DICT_OR_MAPPING_ELSE(dict_or_mapping, result, PyDict_GetItemRef, \ + PyMapping_GetOptionalItem, else_block, obj, ref) #define _Py_DICT_OR_MAPPING_SETITEM(dict_or_mapping, obj, ref, result) \ _Py_DICT_OR_MAPPING(dict_or_mapping, result, PyDict_SetItem, \ - PyObject_SetItem, obj, ref) + PyObject_SetItem, obj, ref) #define _Py_DICT_OR_MAPPING_DELITEM(dict_or_mapping, obj, result) \ _Py_DICT_OR_MAPPING(dict_or_mapping, result, PyDict_DelItem, \ - PyMapping_DelItem, obj) + PyMapping_DelItem, obj) #define _Py_DICT_OR_MAPPING_CONTAINS(dict_or_mapping, obj, result) \ _Py_DICT_OR_MAPPING(dict_or_mapping, result, PyDict_Contains, \ - PyMapping_HasKeyWithError, obj) + PyMapping_HasKeyWithError, obj) #define _Py_DICT_OR_MAPPING_CONTAINS_ELSE(dict_or_mapping, obj, result, \ - else_block) _Py_DICT_OR_MAPPING_ELSE(dict_or_mapping, result, \ - PyDict_Contains, PyMapping_HasKeyWithError, else_block, obj) + else_block) \ + _Py_DICT_OR_MAPPING_ELSE(dict_or_mapping, result, PyDict_Contains, \ + PyMapping_HasKeyWithError, else_block, obj) #define _Py_DICT_OR_MAPPING_KEYS(dict_or_mapping, result) \ _Py_DICT_OR_MAPPING(dict_or_mapping, result, PyDict_Keys, PyMapping_Keys) From 725a327078ec2388bb9318fbe6b28a4290af5dca Mon Sep 17 00:00:00 2001 From: Ben Hsing Date: Fri, 12 Jul 2024 10:51:11 +0800 Subject: [PATCH 49/49] fixed typo --- .../2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst b/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst index cf6656c3352b45..33d79882071055 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2024-07-05-16-24-14.gh-issue-121306.nUBgho.rst @@ -1,2 +1,2 @@ -Allow a mapping to be the `__dict__` attribute of any object. This allows a mapping to be the global namespace of a module. It also allows a mapping to be passed as the *globals* argument for the built-in functions :func:`exec` and :func:`eval`. +Allow a mapping to be the :attr:`~object.__dict__` attribute of any object. This allows a mapping to be the global namespace of a module. It also allows a mapping to be passed as the *globals* argument for the built-in functions :func:`exec` and :func:`eval`. Patch by Ben Hsing