Add frozendict.__getnewargs__() method.
MyStr, MyUnicode,
MyTuple, MyList, MyDict, MySet, MyFrozenSet]
+try:
+ frozendict
+except NameError:
+ # Python 3.14 and older
+ pass
+else:
+ class MyFrozenDict(dict):
+ sample = frozendict({"a": 1, "b": 2})
+ myclasses.append(MyFrozenDict)
+
+
# For test_newobj_overridden_new
class MyIntWithNew(int):
def __new__(cls, value):
self.assertEqual(list(x[0].attr.keys()), [1])
self.assertIs(x[0].attr[1], x)
- def _test_recursive_collection_and_inst(self, factory, oldminproto=None):
+ def _test_recursive_collection_and_inst(self, factory, oldminproto=None,
+ minprotocol=0):
if self.py_version < (3, 0):
self.skipTest('"classic" classes are not interoperable with Python 2')
# Mutable object containing a collection containing the original
# object.
+ protocols = range(minprotocol, pickle.HIGHEST_PROTOCOL + 1)
o = Object()
o.attr = factory([o])
t = type(o.attr)
def test_recursive_dict_and_inst(self):
self._test_recursive_collection_and_inst(dict.fromkeys, oldminproto=0)
+ def test_recursive_frozendict_and_inst(self):
+ if self.py_version < (3, 15):
+ self.skipTest('need frozendict')
+ self._test_recursive_collection_and_inst(frozendict.fromkeys, minprotocol=2)
+
def test_recursive_set_and_inst(self):
self._test_recursive_collection_and_inst(set)
def test_recursive_frozenset_subclass_and_inst(self):
self._test_recursive_collection_and_inst(MyFrozenSet)
+ def _test_recursive_collection_in_key(self, factory, minprotocol=0):
+ protocols = range(minprotocol, pickle.HIGHEST_PROTOCOL + 1)
+ key = Object()
+ o = factory({key: 1})
+ key.attr = o
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ s = self.dumps(o, proto)
+ x = self.loads(s)
+ keys = list(x.keys())
+ self.assertEqual(len(keys), 1)
+ self.assertIs(keys[0].attr, x)
+
+ def test_recursive_frozendict_in_key(self):
+ self._test_recursive_collection_in_key(frozendict, minprotocol=2)
+
+ def test_recursive_frozendict_subclass_in_key(self):
+ self._test_recursive_collection_in_key(MyFrozenDict)
+
+ def _test_recursive_collection_in_value(self, factory, minprotocol=0):
+ protocols = range(minprotocol, pickle.HIGHEST_PROTOCOL + 1)
+ o = factory(key=[])
+ o['key'].append(o)
+ for proto in protocols:
+ with self.subTest(proto=proto):
+ s = self.dumps(o, proto)
+ x = self.loads(s)
+ self.assertEqual(len(x['key']), 1)
+ self.assertIs(x['key'][0], x)
+
+ def test_recursive_frozendict_in_value(self):
+ self._test_recursive_collection_in_value(frozendict, minprotocol=2)
+
+ def test_recursive_frozendict_subclass_in_value(self):
+ self._test_recursive_collection_in_value(MyFrozenDict)
+
def test_recursive_inst_state(self):
# Mutable object containing itself.
y = REX_state()
pass
+class FrozenDictSlots(frozendict):
+ __slots__ = ('slot_attr',)
+
+
class FrozenDictTests(unittest.TestCase):
def test_copy(self):
d = frozendict(x=1, y=2)
d = frozendict(x=1, y=2)
self.assertEqual(repr(d), "frozendict({'x': 1, 'y': 2})")
- class MyFrozenDict(frozendict):
- pass
- d = MyFrozenDict(x=1, y=2)
- self.assertEqual(repr(d), "MyFrozenDict({'x': 1, 'y': 2})")
+ d = FrozenDict(x=1, y=2)
+ self.assertEqual(repr(d), "FrozenDict({'x': 1, 'y': 2})")
def test_hash(self):
# hash() doesn't rely on the items order
self.assertEqual(type(fd), DictSubclass)
self.assertEqual(created, frozendict(x=1))
+ def test_pickle(self):
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ for fd in (
+ frozendict(),
+ frozendict(x=1, y=2),
+ FrozenDict(x=1, y=2),
+ FrozenDictSlots(x=1, y=2),
+ ):
+ if type(fd) == FrozenDict:
+ fd.attr = 123
+ if type(fd) == FrozenDictSlots:
+ fd.slot_attr = 456
+ with self.subTest(fd=fd, proto=proto):
+ if proto >= 2:
+ p = pickle.dumps(fd, proto)
+ fd2 = pickle.loads(p)
+ self.assertEqual(fd2, fd)
+ self.assertEqual(type(fd2), type(fd))
+ if type(fd) == FrozenDict:
+ self.assertEqual(fd2.attr, 123)
+ if type(fd) == FrozenDictSlots:
+ self.assertEqual(fd2.slot_attr, 456)
+ else:
+ # protocol 0 and 1 don't support frozendict
+ with self.assertRaises(TypeError):
+ pickle.dumps(fd, proto)
+
+ def test_pickle_iter(self):
+ fd = frozendict(c=1, b=2, a=3, d=4, e=5, f=6)
+ for method_name in (None, 'keys', 'values', 'items'):
+ if method_name is not None:
+ meth = getattr(fd, method_name)
+ else:
+ meth = lambda: fd
+ expected = list(meth())[1:]
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.subTest(method_name=method_name, protocol=proto):
+ it = iter(meth())
+ next(it)
+ p = pickle.dumps(it, proto)
+ unpickled = pickle.loads(p)
+ self.assertEqual(list(unpickled), expected)
+ self.assertEqual(list(it), expected)
+
if __name__ == "__main__":
unittest.main()
// --- frozendict implementation ---------------------------------------------
+static PyObject *
+frozendict_getnewargs(PyObject *op, PyObject *Py_UNUSED(dummy))
+{
+ // Call dict(op): convert 'op' frozendict to a dict
+ PyObject *arg = PyObject_CallOneArg((PyObject*)&PyDict_Type, op);
+ if (arg == NULL) {
+ return NULL;
+ }
+ return Py_BuildValue("(N)", arg);
+}
+
+
static PyNumberMethods frozendict_as_number = {
.nb_or = frozendict_or,
};
DICT_COPY_METHODDEF
DICT___REVERSED___METHODDEF
{"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
+ {"__getnewargs__", frozendict_getnewargs, METH_NOARGS},
{NULL, NULL} /* sentinel */
};