From d2e866f25f878d230abf2b8aae160066e8660a22 Mon Sep 17 00:00:00 2001 From: Florent Gallaire Date: Thu, 11 Jun 2026 19:45:26 +0200 Subject: [PATCH] =?UTF-8?q?locals():=20return=20a=20dict=20snapshot=20?= =?UTF-8?q?=E2=80=94=20the=20raw=20frame=20object=20is=20not=20a=20Python?= =?UTF-8?q?=20mapping=20('x'=20in=20locals()=20raised=20TypeError)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- www/src/py_builtin_functions.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/www/src/py_builtin_functions.js b/www/src/py_builtin_functions.js index 7822fa774..f811c7b4c 100644 --- a/www/src/py_builtin_functions.js +++ b/www/src/py_builtin_functions.js @@ -1292,10 +1292,20 @@ _b_.locals = function() { var locals_obj = $B.frame_obj.frame[1] // In a class body, locals() is a proxy around a dict(-like) object var class_locals = locals_obj.$target - if (class_locals) { + if (class_locals && $B.get_class(class_locals) === _b_.dict) { return class_locals } - return locals_obj + // CPython's locals() returns a dict snapshot; the raw frame object is + // not a Python mapping ('x' in locals() raised "argument of type ... + // is not a container or iterable"). Skip frame infrastructure keys. + var d = $B.empty_dict() + for (var key in locals_obj) { + if (key.startsWith('$') || key == '__class__' || key == 'ob_type') { + continue + } + _b_.dict.$setitem(d, key, locals_obj[key]) + } + return d }