https://github.com/python/cpython/commit/9e083b57eeb0282f8c5f7bbeec3750b51cf80dcc
commit: 9e083b57eeb0282f8c5f7bbeec3750b51cf80dcc
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-02-21T17:00:23+01:00
summary:

gh-141510: Test frozendict C API (#145081)

Add tests on functions:

* PyAnyDict_Check()
* PyAnyDict_CheckExact()
* PyFrozenDict_Check()
* PyFrozenDict_CheckExact()
* PyFrozenDict_New()

files:
M Lib/test/test_capi/test_dict.py
M Modules/_testcapi/dict.c

diff --git a/Lib/test/test_capi/test_dict.py b/Lib/test/test_capi/test_dict.py
index e726e3d813d888..bdd7aa9819fc48 100644
--- a/Lib/test/test_capi/test_dict.py
+++ b/Lib/test/test_capi/test_dict.py
@@ -26,6 +26,19 @@ def gen():
     yield 'c'
 
 
+class FrozenDictSubclass(frozendict):
+    pass
+
+
+DICT_TYPES = (dict, DictSubclass, OrderedDict)
+FROZENDICT_TYPES = (frozendict, FrozenDictSubclass)
+ANYDICT_TYPES = DICT_TYPES + FROZENDICT_TYPES
+MAPPING_TYPES = (UserDict,)
+NOT_FROZENDICT_TYPES = DICT_TYPES + MAPPING_TYPES
+NOT_ANYDICT_TYPES = MAPPING_TYPES
+OTHER_TYPES = (lambda: [1], lambda: 42, object)  # (list, int, object)
+
+
 class CAPITest(unittest.TestCase):
 
     def test_dict_check(self):
@@ -545,6 +558,61 @@ def test_dict_popstring(self):
         # CRASHES dict_popstring({}, NULL)
         # CRASHES dict_popstring({"a": 1}, NULL)
 
+    def test_frozendict_check(self):
+        # Test PyFrozenDict_Check()
+        check = _testcapi.frozendict_check
+        for dict_type in FROZENDICT_TYPES:
+            self.assertTrue(check(dict_type(x=1)))
+        for dict_type in NOT_FROZENDICT_TYPES + OTHER_TYPES:
+            self.assertFalse(check(dict_type()))
+        # CRASHES check(NULL)
+
+    def test_frozendict_checkexact(self):
+        # Test PyFrozenDict_CheckExact()
+        check = _testcapi.frozendict_checkexact
+        for dict_type in FROZENDICT_TYPES:
+            self.assertEqual(check(dict_type(x=1)), dict_type == frozendict)
+        for dict_type in NOT_FROZENDICT_TYPES + OTHER_TYPES:
+            self.assertFalse(check(dict_type()))
+        # CRASHES check(NULL)
+
+    def test_anydict_check(self):
+        # Test PyAnyDict_Check()
+        check = _testcapi.anydict_check
+        for dict_type in ANYDICT_TYPES:
+            self.assertTrue(check(dict_type({1: 2})))
+        for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES:
+            self.assertFalse(check(test_type()))
+        # CRASHES check(NULL)
+
+    def test_anydict_checkexact(self):
+        # Test PyAnyDict_CheckExact()
+        check = _testcapi.anydict_checkexact
+        for dict_type in ANYDICT_TYPES:
+            self.assertEqual(check(dict_type(x=1)),
+                             dict_type in (dict, frozendict))
+        for test_type in NOT_ANYDICT_TYPES + OTHER_TYPES:
+            self.assertFalse(check(test_type()))
+        # CRASHES check(NULL)
+
+    def test_frozendict_new(self):
+        # Test PyFrozenDict_New()
+        frozendict_new = _testcapi.frozendict_new
+
+        for dict_type in ANYDICT_TYPES:
+            dct = frozendict_new(dict_type({'x': 1}))
+            self.assertEqual(dct, frozendict(x=1))
+            self.assertIs(type(dct), frozendict)
+
+        dct = frozendict_new([('x', 1), ('y', 2)])
+        self.assertEqual(dct, frozendict(x=1, y=2))
+        self.assertIs(type(dct), frozendict)
+
+        # PyFrozenDict_New(NULL) creates an empty dictionary
+        dct = frozendict_new(NULL)
+        self.assertEqual(dct, frozendict())
+        self.assertIs(type(dct), frozendict)
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Modules/_testcapi/dict.c b/Modules/_testcapi/dict.c
index b7c73d7332bd4e..172591b03182ab 100644
--- a/Modules/_testcapi/dict.c
+++ b/Modules/_testcapi/dict.c
@@ -258,6 +258,43 @@ test_dict_iteration(PyObject* self, PyObject 
*Py_UNUSED(ignored))
 }
 
 
+static PyObject *
+frozendict_check(PyObject *self, PyObject *obj)
+{
+    NULLABLE(obj);
+    return PyLong_FromLong(PyFrozenDict_Check(obj));
+}
+
+static PyObject *
+frozendict_checkexact(PyObject *self, PyObject *obj)
+{
+    NULLABLE(obj);
+    return PyLong_FromLong(PyFrozenDict_CheckExact(obj));
+}
+
+static PyObject *
+anydict_check(PyObject *self, PyObject *obj)
+{
+    NULLABLE(obj);
+    return PyLong_FromLong(PyAnyDict_Check(obj));
+}
+
+static PyObject *
+anydict_checkexact(PyObject *self, PyObject *obj)
+{
+    NULLABLE(obj);
+    return PyLong_FromLong(PyAnyDict_CheckExact(obj));
+}
+
+
+static PyObject *
+frozendict_new(PyObject *self, PyObject *obj)
+{
+    NULLABLE(obj);
+    return PyFrozenDict_New(obj);
+}
+
+
 static PyMethodDef test_methods[] = {
     {"dict_containsstring", dict_containsstring, METH_VARARGS},
     {"dict_getitemref", dict_getitemref, METH_VARARGS},
@@ -269,6 +306,11 @@ static PyMethodDef test_methods[] = {
     {"dict_popstring", dict_popstring, METH_VARARGS},
     {"dict_popstring_null", dict_popstring_null, METH_VARARGS},
     {"test_dict_iteration",     test_dict_iteration,             METH_NOARGS},
+    {"frozendict_check", frozendict_check, METH_O},
+    {"frozendict_checkexact", frozendict_checkexact, METH_O},
+    {"anydict_check", anydict_check, METH_O},
+    {"anydict_checkexact", anydict_checkexact, METH_O},
+    {"frozendict_new", frozendict_new, METH_O},
     {NULL},
 };
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to