Updated patch and more justifications.

New patch:
 - dict doesn't inherit from frozendict anymore
 - frozendict is a subclass of collections.abc.Mutable
 - more tests

>  * frozendict.__hash__ computes hash(frozenset(self.items())) and
> caches the result is its private hash attribute

hash(frozenset(self.items())) is preferred over
hash(sorted(self.items())) because keys and values may be unorderable.
frozenset() is faster than sorted(): O(n) vs O(n*log(n)).

frozendict hash doesn't care of the item order creation:

>>> a=frozendict.fromkeys('ai')
>>> a
frozendict({'a': None, 'i': None})
>>> b=frozendict.fromkeys('ia')
>>> b
frozendict({'i': None, 'a': None})
>>> hash(a) == hash(b)
True
>>> a == b
True
>>> tuple(a.items()) == tuple(b.items())
False

frozendict supports unorderable keys and values:

>>> hash(frozendict({b'abc': 1, 'abc': 2}))
935669091
>>> hash(frozendict({1: b'abc', 2: 'abc'}))
1319859033

>  * Add a frozendict abstract base class to collections?

I realized that Mapping already exists and so the following patch is enough:

+Mapping.register(frozendict)

> See also the PEP 351.

I read the PEP and the email explaining why it was rejected.

Just to be clear: the PEP 351 tries to freeze an object, try to
convert a mutable or immutable object to an immutable object. Whereas
my frozendict proposition doesn't convert anything: it just raises a
TypeError if you use a mutable key or value.

For example, frozendict({'list': ['a', 'b', 'c']}) doesn't create
frozendict({'list': ('a', 'b', 'c')}) but raises a TypeError.

Victor
diff --git a/Include/dictobject.h b/Include/dictobject.h
--- a/Include/dictobject.h
+++ b/Include/dictobject.h
@@ -84,10 +84,14 @@ struct _dictobject {
     PyDictEntry *ma_table;
     PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, Py_hash_t hash);
     PyDictEntry ma_smalltable[PyDict_MINSIZE];
+
+    /* only used by frozendict */
+    Py_hash_t hash;
 };
 #endif /* Py_LIMITED_API */
 
 PyAPI_DATA(PyTypeObject) PyDict_Type;
+PyAPI_DATA(PyTypeObject) PyFrozenDict_Type;
 PyAPI_DATA(PyTypeObject) PyDictIterKey_Type;
 PyAPI_DATA(PyTypeObject) PyDictIterValue_Type;
 PyAPI_DATA(PyTypeObject) PyDictIterItem_Type;
@@ -97,7 +101,12 @@ PyAPI_DATA(PyTypeObject) PyDictValues_Ty
 
 #define PyDict_Check(op) \
                  PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_DICT_SUBCLASS)
+#define PyFrozenDict_Check(op)             \
+    (PyDict_Check(op) ||                   \
+     Py_TYPE(op) == &PyFrozenDict_Type ||  \
+     PyType_IsSubtype(Py_TYPE(op), &PyFrozenDict_Type))
 #define PyDict_CheckExact(op) (Py_TYPE(op) == &PyDict_Type)
+#define PyFrozenDict_CheckExact(op) (Py_TYPE(op) == &PyFrozenDict_Type)
 #define PyDictKeys_Check(op) (Py_TYPE(op) == &PyDictKeys_Type)
 #define PyDictItems_Check(op) (Py_TYPE(op) == &PyDictItems_Type)
 #define PyDictValues_Check(op) (Py_TYPE(op) == &PyDictValues_Type)
diff --git a/Lib/collections/abc.py b/Lib/collections/abc.py
--- a/Lib/collections/abc.py
+++ b/Lib/collections/abc.py
@@ -401,6 +401,7 @@ class Mapping(Sized, Iterable, Container
     def __ne__(self, other):
         return not (self == other)
 
+Mapping.register(frozendict)
 
 class MappingView(Sized):
 
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -788,11 +788,66 @@ class Dict(dict):
 class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol):
     type2test = Dict
 
+
+class FrozenDictTests(unittest.TestCase):
+    def test_constructor(self):
+        # unorderable keys and values
+        frozendict(x=b'abc', y='abc')
+        frozendict({b'abc': 1, 'abc': 2})
+
+        # hashable keys and values
+        class Hashable:
+            pass
+        for hashable in (5, 1.0, "abc", (1, 2), Hashable()):
+            frozendict(key=hashable)
+            frozendict(hashable=0)
+
+        # not hashable keys or values
+        class NotHashable:
+            def __hash__(self):
+                raise TypeError("not hashable")
+        for not_hashable in ([], {}, NotHashable()):
+            self.assertRaises(TypeError, frozendict, key=not_hashable)
+            self.assertRaises(TypeError, frozendict, [(not_hashable, 0)])
+            self.assertRaises(TypeError, frozendict, [(0, not_hashable)])
+
+    def test_copy(self):
+        self.assertIsInstance(frozendict().copy(),
+                              frozendict)
+        self.assertEqual(frozendict(x=1, y=2).copy(),
+                         frozendict(x=1, y=2))
+
+    def test_dir(self):
+        self.assertEqual(
+            set(dir(frozendict)) - set(dir(object)),
+            {'__contains__', '__getitem__', '__iter__', '__len__',
+             'copy', 'fromkeys', 'get', 'items', 'keys', 'values'})
+
+    def test_fromkeys(self):
+        self.assertEqual(frozendict.fromkeys('ai'),
+                         frozendict(a=None, i=None))
+
+    def test_hash(self):
+        self.assertEqual(hash(frozendict()),
+                         hash(frozenset()))
+        self.assertEqual(hash(frozendict({1: 2})),
+                         hash(frozenset({(1, 2)})))
+        a = frozendict.fromkeys('ai')
+        b = frozendict.fromkeys('ia')
+        self.assertEqual(hash(a), hash(b))
+        self.assertNotEqual(tuple(a.items()), tuple(b.items()))
+
+    def test_repr(self):
+        self.assertEqual(repr(frozendict()), "frozendict({})")
+        self.assertEqual(repr(frozendict(x=1)), "frozendict({'x': 1})")
+
+
 def test_main():
     support.run_unittest(
         DictTest,
         GeneralMappingTests,
         SubclassMappingTests,
+        FrozenDictTests,
     )
 
 if __name__ == "__main__":
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -10,7 +10,6 @@
 #include "Python.h"
 #include "stringlib/eq.h"
 
-
 /* Set a key error with the specified argument, wrapping it in a
  * tuple automatically so that tuple keys are not unpacked as the
  * exception arguments. */
@@ -25,6 +24,25 @@ set_key_error(PyObject *arg)
     Py_DECREF(tup);
 }
 
+static int
+check_immutable(PyObject *obj)
+{
+    /* fast-path for common known immutable types */
+    if (PyLong_Check(obj) || PyFloat_Check(obj) || PyUnicode_Check(obj)
+        || PyTuple_Check(obj))
+        return 0;
+    if (PyObject_Hash(obj) != -1)
+        return 0;
+    else
+        return -1;
+}
+
+static int
+dict_merge(PyObject *a, PyObject *b, int override, int is_frozen);
+static int
+dict_mergefromseq2(PyObject *d, PyObject *seq2, int override, int is_frozen);
+static PyObject *frozendict_new(void);
+
 /* Define this out if you don't want conversion statistics on exit. */
 #undef SHOW_CONVERSION_COUNTS
 
@@ -461,7 +479,7 @@ _PyDict_HasOnlyStringKeys(PyObject *dict
 {
     Py_ssize_t pos = 0;
     PyObject *key, *value;
-    assert(PyDict_Check(dict));
+    assert(PyFrozenDict_Check(dict));
     /* Shortcut */
     if (((PyDictObject *)dict)->ma_lookup == lookdict_unicode)
         return 1;
@@ -523,10 +541,13 @@ Used by insertdict.
 */
 static int
 insertdict_by_entry(register PyDictObject *mp, PyObject *key, Py_hash_t hash,
-                    PyDictEntry *ep, PyObject *value)
+                    PyDictEntry *ep, PyObject *value, int is_frozen)
 {
     PyObject *old_value;
 
+    if (is_frozen && check_immutable(value) == -1)
+        return -1;
+
     MAINTAIN_TRACKING(mp, key, value);
     if (ep->me_value != NULL) {
         old_value = ep->me_value;
@@ -557,10 +578,14 @@ Eats a reference to key and one to value
 Returns -1 if an error occurred, or 0 on success.
 */
 static int
-insertdict(register PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value)
+insertdict(register PyDictObject *mp, PyObject *key,
+           Py_hash_t hash, PyObject *value, int is_frozen)
 {
     register PyDictEntry *ep;
 
+    if (is_frozen && check_immutable(value) == -1)
+        return -1;
+
     assert(mp->ma_lookup != NULL);
     ep = mp->ma_lookup(mp, key, hash);
     if (ep == NULL) {
@@ -568,7 +593,7 @@ insertdict(register PyDictObject *mp, Py
         Py_DECREF(value);
         return -1;
     }
-    return insertdict_by_entry(mp, key, hash, ep, value);
+    return insertdict_by_entry(mp, key, hash, ep, value, is_frozen);
 }
 
 /*
@@ -725,7 +750,7 @@ PyDict_GetItem(PyObject *op, PyObject *k
     PyDictObject *mp = (PyDictObject *)op;
     PyDictEntry *ep;
     PyThreadState *tstate;
-    if (!PyDict_Check(op))
+    if (!PyFrozenDict_Check(op))
         return NULL;
     if (!PyUnicode_CheckExact(key) ||
         (hash = ((PyASCIIObject *) key)->hash) == -1)
@@ -775,7 +800,7 @@ PyDict_GetItemWithError(PyObject *op, Py
     PyDictObject*mp = (PyDictObject *)op;
     PyDictEntry *ep;
 
-    if (!PyDict_Check(op)) {
+    if (!PyFrozenDict_Check(op)) {
         PyErr_BadInternalCall();
         return NULL;
     }
@@ -796,7 +821,8 @@ PyDict_GetItemWithError(PyObject *op, Py
 
 static int
 dict_set_item_by_hash_or_entry(register PyObject *op, PyObject *key,
-                               Py_hash_t hash, PyDictEntry *ep, PyObject *value)
+                               Py_hash_t hash, PyDictEntry *ep, PyObject *value,
+                               int is_frozen)
 {
     register PyDictObject *mp;
     register Py_ssize_t n_used;
@@ -807,11 +833,11 @@ dict_set_item_by_hash_or_entry(register 
     Py_INCREF(value);
     Py_INCREF(key);
     if (ep == NULL) {
-        if (insertdict(mp, key, hash, value) != 0)
+        if (insertdict(mp, key, hash, value, is_frozen) != 0)
             return -1;
     }
     else {
-        if (insertdict_by_entry(mp, key, hash, ep, value) != 0)
+        if (insertdict_by_entry(mp, key, hash, ep, value, is_frozen) != 0)
             return -1;
     }
     /* If we added a key, we can safely resize.  Otherwise just return!
@@ -840,6 +866,27 @@ dict_set_item_by_hash_or_entry(register 
  * remove them.
  */
 int
+basedict_setitem(PyObject *op, PyObject *key, PyObject *value, int is_frozen)
+{
+    register Py_hash_t hash;
+
+    assert(PyFrozenDict_Check(op));
+    assert(key);
+    assert(value);
+    if (PyUnicode_CheckExact(key)) {
+        hash = ((PyASCIIObject *) key)->hash;
+        if (hash == -1)
+            hash = PyObject_Hash(key);
+    }
+    else {
+        hash = PyObject_Hash(key);
+        if (hash == -1)
+            return -1;
+    }
+    return dict_set_item_by_hash_or_entry(op, key, hash, NULL, value, is_frozen);
+}
+
+int
 PyDict_SetItem(register PyObject *op, PyObject *key, PyObject *value)
 {
     register Py_hash_t hash;
@@ -860,7 +907,7 @@ PyDict_SetItem(register PyObject *op, Py
         if (hash == -1)
             return -1;
     }
-    return dict_set_item_by_hash_or_entry(op, key, hash, NULL, value);
+    return dict_set_item_by_hash_or_entry(op, key, hash, NULL, value, 0);
 }
 
 int
@@ -913,9 +960,10 @@ PyDict_Clear(PyObject *op)
     Py_ssize_t i, n;
 #endif
 
-    if (!PyDict_Check(op))
+    if (!PyFrozenDict_Check(op))
         return;
     mp = (PyDictObject *)op;
+
 #ifdef Py_DEBUG
     n = mp->ma_mask + 1;
     i = 0;
@@ -992,7 +1040,7 @@ PyDict_Next(PyObject *op, Py_ssize_t *pp
     register Py_ssize_t mask;
     register PyDictEntry *ep;
 
-    if (!PyDict_Check(op))
+    if (!PyFrozenDict_Check(op))
         return 0;
     i = *ppos;
     if (i < 0)
@@ -1019,7 +1067,7 @@ _PyDict_Next(PyObject *op, Py_ssize_t *p
     register Py_ssize_t mask;
     register PyDictEntry *ep;
 
-    if (!PyDict_Check(op))
+    if (!PyFrozenDict_Check(op))
         return 0;
     i = *ppos;
     if (i < 0)
@@ -1143,6 +1191,18 @@ Done:
     return result;
 }
 
+static PyObject *
+frozendict_repr(PyDictObject *mp)
+{
+    PyObject *repr, *result;
+    repr = dict_repr(mp);
+    if (repr == NULL)
+        return NULL;
+    result = PyUnicode_FromFormat("frozendict(%S)", repr);
+    Py_DECREF(repr);
+    return result;
+}
+
 static Py_ssize_t
 dict_length(PyDictObject *mp)
 {
@@ -1198,6 +1258,11 @@ dict_ass_sub(PyDictObject *mp, PyObject 
         return PyDict_SetItem((PyObject *)mp, v, w);
 }
 
+static PyMappingMethods frozendict_as_mapping = {
+    (lenfunc)dict_length, /*mp_length*/
+    (binaryfunc)dict_subscript, /*mp_subscript*/
+};
+
 static PyMappingMethods dict_as_mapping = {
     (lenfunc)dict_length, /*mp_length*/
     (binaryfunc)dict_subscript, /*mp_subscript*/
@@ -1324,7 +1389,7 @@ dict_items(register PyDictObject *mp)
 }
 
 static PyObject *
-dict_fromkeys(PyObject *cls, PyObject *args)
+basedict_fromkeys(PyObject *cls, PyObject *args, int is_frozen)
 {
     PyObject *seq;
     PyObject *value = Py_None;
@@ -1355,7 +1420,7 @@ dict_fromkeys(PyObject *cls, PyObject *a
         while (_PyDict_Next(seq, &pos, &key, &oldvalue, &hash)) {
             Py_INCREF(key);
             Py_INCREF(value);
-            if (insertdict(mp, key, hash, value)) {
+            if (insertdict(mp, key, hash, value, is_frozen)) {
                 Py_DECREF(d);
                 return NULL;
             }
@@ -1377,7 +1442,7 @@ dict_fromkeys(PyObject *cls, PyObject *a
         while (_PySet_NextEntry(seq, &pos, &key, &hash)) {
             Py_INCREF(key);
             Py_INCREF(value);
-            if (insertdict(mp, key, hash, value)) {
+            if (insertdict(mp, key, hash, value, is_frozen)) {
                 Py_DECREF(d);
                 return NULL;
             }
@@ -1391,9 +1456,9 @@ dict_fromkeys(PyObject *cls, PyObject *a
         return NULL;
     }
 
-    if (PyDict_CheckExact(d)) {
+    if (PyFrozenDict_CheckExact(d) || PyDict_CheckExact(d)) {
         while ((key = PyIter_Next(it)) != NULL) {
-            status = PyDict_SetItem(d, key, value);
+            status = basedict_setitem(d, key, value, is_frozen);
             Py_DECREF(key);
             if (status < 0)
                 goto Fail;
@@ -1418,8 +1483,21 @@ Fail:
     return NULL;
 }
 
+static PyObject *
+frozendict_fromkeys(PyObject *cls, PyObject *args)
+{
+    return basedict_fromkeys(cls, args, 1);
+}
+
+static PyObject *
+dict_fromkeys(PyObject *cls, PyObject *args)
+{
+    return basedict_fromkeys(cls, args, 0);
+}
+
 static int
-dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, char *methname)
+dict_update_common(PyObject *self, PyObject *args, PyObject *kwds,
+                   char *methname, int is_frozen)
 {
     PyObject *arg = NULL;
     int result = 0;
@@ -1430,13 +1508,13 @@ dict_update_common(PyObject *self, PyObj
     else if (arg != NULL) {
         _Py_IDENTIFIER(keys);
         if (_PyObject_HasAttrId(arg, &PyId_keys))
-            result = PyDict_Merge(self, arg, 1);
+            result = dict_merge(self, arg, 1, is_frozen);
         else
-            result = PyDict_MergeFromSeq2(self, arg, 1);
+            result = dict_mergefromseq2(self, arg, 1, is_frozen);
     }
     if (result == 0 && kwds != NULL) {
         if (PyArg_ValidateKeywordArguments(kwds))
-            result = PyDict_Merge(self, kwds, 1);
+            result = dict_merge(self, kwds, 1, is_frozen);
         else
             result = -1;
     }
@@ -1446,7 +1524,7 @@ dict_update_common(PyObject *self, PyObj
 static PyObject *
 dict_update(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    if (dict_update_common(self, args, kwds, "update") != -1)
+    if (dict_update_common(self, args, kwds, "update", 0) != -1)
         Py_RETURN_NONE;
     return NULL;
 }
@@ -1461,8 +1539,8 @@ dict_update(PyObject *self, PyObject *ar
    producing iterable objects of length 2.
 */
 
-int
-PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override)
+static int
+dict_mergefromseq2(PyObject *d, PyObject *seq2, int override, int is_frozen)
 {
     PyObject *it;       /* iter(seq2) */
     Py_ssize_t i;       /* index into seq2 of current element */
@@ -1470,7 +1548,7 @@ PyDict_MergeFromSeq2(PyObject *d, PyObje
     PyObject *fast;     /* item as a 2-tuple or 2-list */
 
     assert(d != NULL);
-    assert(PyDict_Check(d));
+    assert(PyFrozenDict_Check(d));
     assert(seq2 != NULL);
 
     it = PyObject_GetIter(seq2);
@@ -1511,8 +1589,10 @@ PyDict_MergeFromSeq2(PyObject *d, PyObje
         /* Update/merge with this (key, value) pair. */
         key = PySequence_Fast_GET_ITEM(fast, 0);
         value = PySequence_Fast_GET_ITEM(fast, 1);
+        if (is_frozen && check_immutable(value) == -1)
+            goto Fail;
         if (override || PyDict_GetItem(d, key) == NULL) {
-            int status = PyDict_SetItem(d, key, value);
+            int status = basedict_setitem(d, key, value, is_frozen);
             if (status < 0)
                 goto Fail;
         }
@@ -1532,13 +1612,19 @@ Return:
 }
 
 int
+PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override)
+{
+    return dict_mergefromseq2(d, seq2, override, 0);
+}
+
+int
 PyDict_Update(PyObject *a, PyObject *b)
 {
-    return PyDict_Merge(a, b, 1);
+    return dict_merge(a, b, 1, 0);
 }
 
-int
-PyDict_Merge(PyObject *a, PyObject *b, int override)
+static int
+dict_merge(PyObject *a, PyObject *b, int override, int is_frozen)
 {
     register PyDictObject *mp, *other;
     register Py_ssize_t i;
@@ -1549,12 +1635,13 @@ PyDict_Merge(PyObject *a, PyObject *b, i
      * things quite efficiently.  For the latter, we only require that
      * PyMapping_Keys() and PyObject_GetItem() be supported.
      */
-    if (a == NULL || !PyDict_Check(a) || b == NULL) {
+    if (a == NULL || !PyFrozenDict_Check(a) || b == NULL) {
         PyErr_BadInternalCall();
         return -1;
     }
     mp = (PyDictObject*)a;
-    if (PyDict_Check(b)) {
+
+    if (PyFrozenDict_Check(b)) {
         other = (PyDictObject*)b;
         if (other == mp || other->ma_used == 0)
             /* a.update(a) or a.update({}); nothing to do */
@@ -1575,16 +1662,17 @@ PyDict_Merge(PyObject *a, PyObject *b, i
         }
         for (i = 0; i <= other->ma_mask; i++) {
             entry = &other->ma_table[i];
-            if (entry->me_value != NULL &&
-                (override ||
-                 PyDict_GetItem(a, entry->me_key) == NULL)) {
-                Py_INCREF(entry->me_key);
-                Py_INCREF(entry->me_value);
-                if (insertdict(mp, entry->me_key,
-                               entry->me_hash,
-                               entry->me_value) != 0)
-                    return -1;
-            }
+            if (entry->me_value == NULL)
+                continue;
+            if (!override && PyDict_GetItem(a, entry->me_key) != NULL)
+                continue;
+            Py_INCREF(entry->me_key);
+            Py_INCREF(entry->me_value);
+            if (insertdict(mp, entry->me_key,
+                           entry->me_hash,
+                           entry->me_value,
+                           is_frozen) != 0)
+                return -1;
         }
     }
     else {
@@ -1618,7 +1706,13 @@ PyDict_Merge(PyObject *a, PyObject *b, i
                 Py_DECREF(key);
                 return -1;
             }
-            status = PyDict_SetItem(a, key, value);
+            if (is_frozen && check_immutable(value) == -1) {
+                Py_DECREF(iter);
+                Py_DECREF(key);
+                Py_DECREF(value);
+                return -1;
+            }
+            status = basedict_setitem(a, key, value, is_frozen);
             Py_DECREF(key);
             Py_DECREF(value);
             if (status < 0) {
@@ -1634,6 +1728,12 @@ PyDict_Merge(PyObject *a, PyObject *b, i
     return 0;
 }
 
+int
+PyDict_Merge(PyObject *a, PyObject *b, int override)
+{
+    return dict_merge(a, b, override, 0);
+}
+
 static PyObject *
 dict_copy(register PyDictObject *mp)
 {
@@ -1652,16 +1752,33 @@ PyDict_Copy(PyObject *o)
     copy = PyDict_New();
     if (copy == NULL)
         return NULL;
-    if (PyDict_Merge(copy, o, 1) == 0)
+    if (dict_merge(copy, o, 1, 0) == 0)
         return copy;
     Py_DECREF(copy);
     return NULL;
 }
 
+static PyObject *
+frozendict_copy(PyObject *o)
+{
+    PyObject *copy;
+    if (o == NULL || !PyFrozenDict_Check(o)) {
+        PyErr_BadInternalCall();
+        return NULL;
+    }
+    copy = frozendict_new();
+    if (copy == NULL)
+        return NULL;
+    if (dict_merge(copy, o, 1, 1) == 0)
+        return copy;
+    Py_DECREF(copy);
+    return NULL;
+}
+
 Py_ssize_t
 PyDict_Size(PyObject *mp)
 {
-    if (mp == NULL || !PyDict_Check(mp)) {
+    if (mp == NULL || !PyFrozenDict_Check(mp)) {
         PyErr_BadInternalCall();
         return -1;
     }
@@ -1671,7 +1788,7 @@ PyDict_Size(PyObject *mp)
 PyObject *
 PyDict_Keys(PyObject *mp)
 {
-    if (mp == NULL || !PyDict_Check(mp)) {
+    if (mp == NULL || !PyFrozenDict_Check(mp)) {
         PyErr_BadInternalCall();
         return NULL;
     }
@@ -1681,7 +1798,7 @@ PyDict_Keys(PyObject *mp)
 PyObject *
 PyDict_Values(PyObject *mp)
 {
-    if (mp == NULL || !PyDict_Check(mp)) {
+    if (mp == NULL || !PyFrozenDict_Check(mp)) {
         PyErr_BadInternalCall();
         return NULL;
     }
@@ -1691,7 +1808,7 @@ PyDict_Values(PyObject *mp)
 PyObject *
 PyDict_Items(PyObject *mp)
 {
-    if (mp == NULL || !PyDict_Check(mp)) {
+    if (mp == NULL || !PyFrozenDict_Check(mp)) {
         PyErr_BadInternalCall();
         return NULL;
     }
@@ -1746,7 +1863,7 @@ dict_richcompare(PyObject *v, PyObject *
     int cmp;
     PyObject *res;
 
-    if (!PyDict_Check(v) || !PyDict_Check(w)) {
+    if (!PyFrozenDict_Check(v) || !PyFrozenDict_Check(w)) {
         res = Py_NotImplemented;
     }
     else if (op == Py_EQ || op == Py_NE) {
@@ -1832,7 +1949,7 @@ dict_setdefault(register PyDictObject *m
     val = ep->me_value;
     if (val == NULL) {
         if (dict_set_item_by_hash_or_entry((PyObject*)mp, key, hash, ep,
-                                           failobj) == 0)
+                                           failobj, 0) == 0)
             val = failobj;
     }
     Py_XINCREF(val);
@@ -2034,7 +2151,29 @@ PyDoc_STRVAR(items__doc__,
 PyDoc_STRVAR(values__doc__,
              "D.values() -> an object providing a view on D's values");
 
-static PyMethodDef mapp_methods[] = {
+static PyMethodDef frozendict_methods[] = {
+    {"__contains__",(PyCFunction)dict_contains,     METH_O | METH_COEXIST,
+     contains__doc__},
+    {"__getitem__", (PyCFunction)dict_subscript,        METH_O | METH_COEXIST,
+     getitem__doc__},
+    {"__sizeof__",      (PyCFunction)dict_sizeof,       METH_NOARGS,
+     sizeof__doc__},
+    {"get",         (PyCFunction)dict_get,          METH_VARARGS,
+     get__doc__},
+    {"keys",            (PyCFunction)dictkeys_new,      METH_NOARGS,
+    keys__doc__},
+    {"items",           (PyCFunction)dictitems_new,     METH_NOARGS,
+    items__doc__},
+    {"values",          (PyCFunction)dictvalues_new,    METH_NOARGS,
+    values__doc__},
+    {"fromkeys",        (PyCFunction)frozendict_fromkeys,     METH_VARARGS | METH_CLASS,
+     fromkeys__doc__},
+    {"copy",            (PyCFunction)frozendict_copy,   METH_NOARGS,
+     copy__doc__},
+    {NULL,              NULL}   /* sentinel */
+};
+
+static PyMethodDef dict_methods[] = {
     {"__contains__",(PyCFunction)dict_contains,     METH_O | METH_COEXIST,
      contains__doc__},
     {"__getitem__", (PyCFunction)dict_subscript,        METH_O | METH_COEXIST,
@@ -2138,10 +2277,37 @@ dict_new(PyTypeObject *type, PyObject *a
     return self;
 }
 
+static PyObject *
+frozendict_new(void)
+{
+    PyObject *self;
+    PyDictObject *d;
+
+    self = dict_new(&PyFrozenDict_Type, NULL, NULL);
+    if (self == NULL)
+        return NULL;
+    d = (PyDictObject *)self;
+    d->hash = -1;
+    return self;
+}
+
+static PyObject *
+frozendict___new__(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    PyObject *self = frozendict_new();
+    if (self == NULL)
+        return NULL;
+    if (dict_update_common(self, args, kwds, "frozendict", 1) == -1) {
+        Py_DECREF(self);
+        return NULL;
+    }
+    return self;
+}
+
 static int
 dict_init(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    return dict_update_common(self, args, kwds, "dict");
+    return dict_update_common(self, args, kwds, "dict", 0);
 }
 
 static PyObject *
@@ -2150,6 +2316,86 @@ dict_iter(PyDictObject *dict)
     return dictiter_new(dict, &PyDictIterKey_Type);
 }
 
+static Py_hash_t
+frozendict_hash(PyObject *self)
+{
+    PyDictObject *frozendict = (PyDictObject *)self;
+    PyObject *items, *frozen_items;
+    Py_hash_t hash;
+
+    if (frozendict->hash != -1)
+        return frozendict->hash;
+
+    items = PyObject_CallMethod(self, "items", "");
+    if (items == NULL)
+        return -1;
+    frozen_items = PyFrozenSet_New(items);
+    Py_DECREF(items);
+    if (frozen_items == NULL)
+        return -1;
+    hash = PyObject_Hash(frozen_items);
+    Py_DECREF(frozen_items);
+    if (hash == -1)
+        return -1;
+
+    frozendict->hash = hash;
+    return hash;
+}
+
+PyDoc_STRVAR(frozendict_doc,
+"frozendict() -> new empty frozen dictionary\n"
+"frozendict(mapping) -> new frozen dictionary initialized from a mapping object's\n"
+"    (key, value) pairs\n"
+"frozendict(iterable) -> new frozen dictionary initialized as if via:\n"
+"    d = {}\n"
+"    for k, v in iterable:\n"
+"        d[k] = v\n"
+"frozendict(**kwargs) -> new frozen dictionary initialized with the name=value\n"
+"    pairs in the keyword argument list.  For example:  frozendict(one=1, two=2)");
+
+PyTypeObject PyFrozenDict_Type = {
+    PyVarObject_HEAD_INIT(&PyType_Type, 0)
+    "frozendict",
+    sizeof(PyDictObject),
+    0,
+    (destructor)dict_dealloc,                   /* tp_dealloc */
+    0,                                          /* tp_print */
+    0,                                          /* tp_getattr */
+    0,                                          /* tp_setattr */
+    0,                                          /* tp_reserved */
+    (reprfunc)frozendict_repr,                  /* tp_repr */
+    0,                                          /* tp_as_number */
+    &dict_as_sequence,                          /* tp_as_sequence */
+    &frozendict_as_mapping,                     /* tp_as_mapping */
+    frozendict_hash,                            /* tp_hash */
+    0,                                          /* tp_call */
+    0,                                          /* tp_str */
+    PyObject_GenericGetAttr,                    /* tp_getattro */
+    0,                                          /* tp_setattro */
+    0,                                          /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+        Py_TPFLAGS_BASETYPE,                    /* tp_flags */
+    frozendict_doc,                             /* tp_doc */
+    dict_traverse,                              /* tp_traverse */
+    dict_tp_clear,                              /* tp_clear */
+    dict_richcompare,                           /* tp_richcompare */
+    0,                                          /* tp_weaklistoffset */
+    (getiterfunc)dict_iter,                     /* tp_iter */
+    0,                                          /* tp_iternext */
+    frozendict_methods,                         /* tp_methods */
+    0,                                          /* tp_members */
+    0,                                          /* tp_getset */
+    0,                                          /* tp_base */
+    0,                                          /* tp_dict */
+    0,                                          /* tp_descr_get */
+    0,                                          /* tp_descr_set */
+    0,                                          /* tp_dictoffset */
+    0,                                          /* tp_init */
+    PyType_GenericAlloc,                        /* tp_alloc */
+    frozendict___new__,                         /* tp_new */
+    PyObject_GC_Del,                            /* tp_free */
+};
+
 PyDoc_STRVAR(dictionary_doc,
 "dict() -> new empty dictionary\n"
 "dict(mapping) -> new dictionary initialized from a mapping object's\n"
@@ -2190,7 +2436,7 @@ PyTypeObject PyDict_Type = {
     0,                                          /* tp_weaklistoffset */
     (getiterfunc)dict_iter,                     /* tp_iter */
     0,                                          /* tp_iternext */
-    mapp_methods,                               /* tp_methods */
+    dict_methods,                               /* tp_methods */
     0,                                          /* tp_members */
     0,                                          /* tp_getset */
     0,                                          /* tp_base */
@@ -2324,7 +2570,7 @@ static PyObject *dictiter_iternextkey(di
 
     if (d == NULL)
         return NULL;
-    assert (PyDict_Check(d));
+    assert (PyFrozenDict_Check(d));
 
     if (di->di_used != d->ma_used) {
         PyErr_SetString(PyExc_RuntimeError,
@@ -2396,7 +2642,7 @@ static PyObject *dictiter_iternextvalue(
 
     if (d == NULL)
         return NULL;
-    assert (PyDict_Check(d));
+    assert (PyFrozenDict_Check(d));
 
     if (di->di_used != d->ma_used) {
         PyErr_SetString(PyExc_RuntimeError,
@@ -2468,7 +2714,7 @@ static PyObject *dictiter_iternextitem(d
 
     if (d == NULL)
         return NULL;
-    assert (PyDict_Check(d));
+    assert (PyFrozenDict_Check(d));
 
     if (di->di_used != d->ma_used) {
         PyErr_SetString(PyExc_RuntimeError,
@@ -2589,7 +2835,7 @@ dictview_new(PyObject *dict, PyTypeObjec
         PyErr_BadInternalCall();
         return NULL;
     }
-    if (!PyDict_Check(dict)) {
+    if (!PyFrozenDict_Check(dict)) {
         /* XXX Get rid of this restriction later */
         PyErr_Format(PyExc_TypeError,
                      "%s() requires a dict argument, not '%s'",
diff --git a/Objects/object.c b/Objects/object.c
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -1620,6 +1620,9 @@ _Py_ReadyTypes(void)
     if (PyType_Ready(&PyRange_Type) < 0)
         Py_FatalError("Can't initialize range type");
 
+    if (PyType_Ready(&PyFrozenDict_Type) < 0)
+        Py_FatalError("Can't initialize frozendict type");
+
     if (PyType_Ready(&PyDict_Type) < 0)
         Py_FatalError("Can't initialize dict type");
 
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -2396,6 +2396,7 @@ _PyBuiltin_Init(void)
     SETBUILTIN("enumerate",             &PyEnum_Type);
     SETBUILTIN("filter",                &PyFilter_Type);
     SETBUILTIN("float",                 &PyFloat_Type);
+    SETBUILTIN("frozendict",            &PyFrozenDict_Type);
     SETBUILTIN("frozenset",             &PyFrozenSet_Type);
     SETBUILTIN("property",              &PyProperty_Type);
     SETBUILTIN("int",                   &PyLong_Type);
_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com

Reply via email to