]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-136492: Add `FrameLocalsProxyType` to `types` (GH-136546)
authorPeter Bierma <zintensitydev@gmail.com>
Sun, 20 Jul 2025 18:49:00 +0000 (20:49 +0200)
committerGitHub <noreply@github.com>
Sun, 20 Jul 2025 18:49:00 +0000 (20:49 +0200)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Doc/library/types.rst
Doc/whatsnew/3.15.rst
Lib/test/test_inspect/test_inspect.py
Lib/test/test_types.py
Lib/types.py
Misc/NEWS.d/next/Library/2025-07-11-10-23-44.gh-issue-136492.BVi5h0.rst [new file with mode: 0644]
Modules/_typesmodule.c
Objects/frameobject.c

index 2bedd7fdd3c8c8c4b93d76d5b675ec1d6fec428b..207024a76199025f1ccc65040f77b8d1ee8403a5 100644 (file)
@@ -333,6 +333,16 @@ Standard names are defined for the following types:
    :attr:`tb.tb_frame <traceback.tb_frame>` if ``tb`` is a traceback object.
 
 
+.. data:: FrameLocalsProxyType
+
+   The type of frame locals proxy objects, as found on the
+   :attr:`frame.f_locals` attribute.
+
+   .. versionadded:: next
+
+   .. seealso:: :pep:`667`
+
+
 .. data:: GetSetDescriptorType
 
    The type of objects defined in extension modules with ``PyGetSetDef``, such
index 7e47fa263d9a5eb1ff4cd2e3f54038f1b1a12d0b..4d4fb77ad4f030d675866d6fdc5eab108b30c002 100644 (file)
@@ -312,6 +312,15 @@ tarfile
   and :cve:`2025-4435`.)
 
 
+types
+------
+
+* Expose the write-through :func:`locals` proxy type
+  as :data:`types.FrameLocalsProxyType`.
+  This represents the type of the :attr:`frame.f_locals` attribute,
+  as described in :pep:`667`.
+
+
 unittest
 --------
 
index 4f3983d83c7c067b932a39c66f13faadb63a285f..30e01b8cd87a75475907d71cc98fd550080e26a2 100644 (file)
@@ -5786,6 +5786,7 @@ class TestSignatureDefinitions(unittest.TestCase):
             'AsyncGeneratorType': {'athrow'},
             'CoroutineType': {'throw'},
             'GeneratorType': {'throw'},
+            'FrameLocalsProxyType': {'setdefault', 'pop', 'get'},
         }
         self._test_module_has_signatures(types,
                 unsupported_signature=unsupported_signature,
index fccdcc975e6c43ecd2100922b1aec40ec1fe69d9..9b0ae709d7968d030d5219fff272a0c97710005a 100644 (file)
@@ -53,6 +53,7 @@ class TypesTests(unittest.TestCase):
             'AsyncGeneratorType', 'BuiltinFunctionType', 'BuiltinMethodType',
             'CapsuleType', 'CellType', 'ClassMethodDescriptorType', 'CodeType',
             'CoroutineType', 'EllipsisType', 'FrameType', 'FunctionType',
+            'FrameLocalsProxyType',
             'GeneratorType', 'GenericAlias', 'GetSetDescriptorType',
             'LambdaType', 'MappingProxyType', 'MemberDescriptorType',
             'MethodDescriptorType', 'MethodType', 'MethodWrapperType',
@@ -711,6 +712,16 @@ class TypesTests(unittest.TestCase):
         """
         assert_python_ok("-c", code)
 
+    def test_frame_locals_proxy_type(self):
+        self.assertIsInstance(types.FrameLocalsProxyType, type)
+        self.assertIsInstance(types.FrameLocalsProxyType.__doc__, str)
+        self.assertEqual(types.FrameLocalsProxyType.__module__, 'builtins')
+        self.assertEqual(types.FrameLocalsProxyType.__name__, 'FrameLocalsProxy')
+
+        frame = inspect.currentframe()
+        self.assertIsNotNone(frame)
+        self.assertIsInstance(frame.f_locals, types.FrameLocalsProxyType)
+
 
 class UnionTests(unittest.TestCase):
 
index cf0549315a78144b2a3efbde87928a9fd8ae65a0..f96c75b46daba7e3b099135a968cf15a35ddf8d1 100644 (file)
@@ -58,7 +58,10 @@ except ImportError:
         raise TypeError
     except TypeError as exc:
         TracebackType = type(exc.__traceback__)
-        FrameType = type(exc.__traceback__.tb_frame)
+
+    _f = (lambda: sys._getframe())()
+    FrameType = type(_f)
+    FrameLocalsProxyType = type(_f.f_locals)
 
     GetSetDescriptorType = type(FunctionType.__code__)
     MemberDescriptorType = type(FunctionType.__globals__)
diff --git a/Misc/NEWS.d/next/Library/2025-07-11-10-23-44.gh-issue-136492.BVi5h0.rst b/Misc/NEWS.d/next/Library/2025-07-11-10-23-44.gh-issue-136492.BVi5h0.rst
new file mode 100644 (file)
index 0000000..7ab5b06
--- /dev/null
@@ -0,0 +1 @@
+Expose :pep:`667`'s :data:`~types.FrameLocalsProxyType` in the :mod:`types` module.
index a30a88196e719219ee7f08faf708d52f0dbfabd0..df6b4c93cb87a66d31a9b7227575ac1ab65f39e8 100644 (file)
@@ -28,6 +28,7 @@ _types_exec(PyObject *m)
     EXPORT_STATIC_TYPE("CoroutineType", PyCoro_Type);
     EXPORT_STATIC_TYPE("EllipsisType", PyEllipsis_Type);
     EXPORT_STATIC_TYPE("FrameType", PyFrame_Type);
+    EXPORT_STATIC_TYPE("FrameLocalsProxyType", PyFrameLocalsProxy_Type);
     EXPORT_STATIC_TYPE("FunctionType", PyFunction_Type);
     EXPORT_STATIC_TYPE("GeneratorType", PyGen_Type);
     EXPORT_STATIC_TYPE("GenericAlias", Py_GenericAliasType);
index 601fc69c4b1f6092e8952df6f4b5312532f5c6ba..97de1e06efe1b20dec01e1537174638cf99d1f98 100644 (file)
@@ -913,6 +913,15 @@ static PyMethodDef framelocalsproxy_methods[] = {
     {NULL, NULL}   /* sentinel */
 };
 
+PyDoc_STRVAR(framelocalsproxy_doc,
+"FrameLocalsProxy($frame)\n"
+"--\n"
+"\n"
+"Create a write-through view of the locals dictionary for a frame.\n"
+"\n"
+"  frame\n"
+"    the frame object to wrap.");
+
 PyTypeObject PyFrameLocalsProxy_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     .tp_name = "FrameLocalsProxy",
@@ -933,6 +942,7 @@ PyTypeObject PyFrameLocalsProxy_Type = {
     .tp_alloc = PyType_GenericAlloc,
     .tp_new = framelocalsproxy_new,
     .tp_free = PyObject_GC_Del,
+    .tp_doc = framelocalsproxy_doc,
 };
 
 PyObject *