summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2024-06-19 16:54:13 (GMT)
committerGitHub <noreply@github.com>2024-06-19 16:54:13 (GMT)
commitd1c673b658977a8e6236feee579308e0ed6a0187 (patch)
tree7202eaae095a36918a8d72503c9bbf9af9786205
parent00257c746c447a2e026b5a2a618f0e033fb90111 (diff)
downloadcpython-d1c673b658977a8e6236feee579308e0ed6a0187.zip
cpython-d1c673b658977a8e6236feee579308e0ed6a0187.tar.gz
cpython-d1c673b658977a8e6236feee579308e0ed6a0187.tar.bz2
GH-120097: Make FrameLocalsProxy a mapping (#120101)
* Register FrameLocalsProxy as a subclass of collections.abc.Mapping * Allow FrameLocalsProxy to matching mapping patterns
-rw-r--r--Lib/_collections_abc.py5
-rw-r--r--Lib/test/test_frame.py12
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2024-06-05-10-32-44.gh-issue-120097.9S2klk.rst2
-rw-r--r--Objects/frameobject.c2
4 files changed, 20 insertions, 1 deletions
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
index 1135e17..75252b3 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -85,6 +85,10 @@ dict_values = type({}.values())
dict_items = type({}.items())
## misc ##
mappingproxy = type(type.__dict__)
+def _get_framelocalsproxy():
+ return type(sys._getframe().f_locals)
+framelocalsproxy = _get_framelocalsproxy()
+del _get_framelocalsproxy
generator = type((lambda: (yield))())
## coroutine ##
async def _coro(): pass
@@ -836,6 +840,7 @@ class Mapping(Collection):
__reversed__ = None
Mapping.register(mappingproxy)
+Mapping.register(framelocalsproxy)
class MappingView(Sized):
diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py
index 42f9382..b7ef6ce 100644
--- a/Lib/test/test_frame.py
+++ b/Lib/test/test_frame.py
@@ -11,6 +11,7 @@ try:
except ImportError:
_testcapi = None
+from collections.abc import Mapping
from test import support
from test.support import import_helper, threading_helper
from test.support.script_helper import assert_python_ok
@@ -418,6 +419,17 @@ class TestFrameLocals(unittest.TestCase):
with self.assertRaises(TypeError):
copy.deepcopy(d)
+ def test_is_mapping(self):
+ x = 1
+ d = sys._getframe().f_locals
+ self.assertIsInstance(d, Mapping)
+ match d:
+ case {"x": value}:
+ self.assertEqual(value, 1)
+ kind = "mapping"
+ case _:
+ kind = "other"
+ self.assertEqual(kind, "mapping")
class TestFrameCApi(unittest.TestCase):
def test_basic(self):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-06-05-10-32-44.gh-issue-120097.9S2klk.rst b/Misc/NEWS.d/next/Core and Builtins/2024-06-05-10-32-44.gh-issue-120097.9S2klk.rst
new file mode 100644
index 0000000..39d829b
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-06-05-10-32-44.gh-issue-120097.9S2klk.rst
@@ -0,0 +1,2 @@
+``FrameLocalsProxy`` now subclasses ``collections.abc.Mapping`` and can be
+matched as a mapping in ``match`` statements
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 5c65007..860669c 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -720,7 +720,7 @@ PyTypeObject PyFrameLocalsProxy_Type = {
.tp_as_mapping = &framelocalsproxy_as_mapping,
.tp_getattro = PyObject_GenericGetAttr,
.tp_setattro = PyObject_GenericSetAttr,
- .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_MAPPING,
.tp_traverse = framelocalsproxy_visit,
.tp_clear = framelocalsproxy_tp_clear,
.tp_richcompare = framelocalsproxy_richcompare,