Issue #14386: Expose the dict_proxy internal type as types.MappingProxyType
diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst
index ac714a6..6bacc32 100644
--- a/Doc/c-api/dict.rst
+++ b/Doc/c-api/dict.rst
@@ -36,11 +36,11 @@
    Return a new empty dictionary, or *NULL* on failure.
 
 
-.. c:function:: PyObject* PyDictProxy_New(PyObject *dict)
+.. c:function:: PyObject* PyDictProxy_New(PyObject *mapping)
 
-   Return a proxy object for a mapping which enforces read-only behavior.
-   This is normally used to create a proxy to prevent modification of the
-   dictionary for non-dynamic class types.
+   Return a :class:`types.MappingProxyType` object for a mapping which
+   enforces read-only behavior.  This is normally used to create a view to
+   prevent modification of the dictionary for non-dynamic class types.
 
 
 .. c:function:: void PyDict_Clear(PyObject *p)
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index 8b8400e..f06f579 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -2258,13 +2258,13 @@
 
    .. method:: items()
 
-      Return a new view of the dictionary's items (``(key, value)`` pairs).  See
-      below for documentation of view objects.
+      Return a new view of the dictionary's items (``(key, value)`` pairs).
+      See the :ref:`documentation of view objects <dict-views>`.
 
    .. method:: keys()
 
-      Return a new view of the dictionary's keys.  See below for documentation of
-      view objects.
+      Return a new view of the dictionary's keys.  See the :ref:`documentation
+      of view objects <dict-views>`.
 
    .. method:: pop(key[, default])
 
@@ -2298,8 +2298,12 @@
 
    .. method:: values()
 
-      Return a new view of the dictionary's values.  See below for documentation of
-      view objects.
+      Return a new view of the dictionary's values.  See the
+      :ref:`documentation of view objects <dict-views>`.
+
+.. seealso::
+   :class:`types.MappingProxyType` can be used to create a read-only view
+   of a :class:`dict`.
 
 
 .. _dict-views:
diff --git a/Doc/library/types.rst b/Doc/library/types.rst
index d4a76b6..0368177 100644
--- a/Doc/library/types.rst
+++ b/Doc/library/types.rst
@@ -85,3 +85,55 @@
 
       In other implementations of Python, this type may be identical to
       ``GetSetDescriptorType``.
+
+.. class:: MappingProxyType(mapping)
+
+   Read-only proxy of a mapping. It provides a dynamic view on the mapping's
+   entries, which means that when the mapping changes, the view reflects these
+   changes.
+
+   .. versionadded:: 3.3
+
+   .. describe:: key in proxy
+
+      Return ``True`` if the underlying mapping has a key *key*, else
+      ``False``.
+
+   .. describe:: proxy[key]
+
+      Return the item of the underlying mapping with key *key*.  Raises a
+      :exc:`KeyError` if *key* is not in the underlying mapping.
+
+   .. describe:: iter(proxy)
+
+      Return an iterator over the keys of the underlying mapping.  This is a
+      shortcut for ``iter(proxy.keys())``.
+
+   .. describe:: len(proxy)
+
+      Return the number of items in the underlying mapping.
+
+   .. method:: copy()
+
+      Return a shallow copy of the underlying mapping.
+
+   .. method:: get(key[, default])
+
+      Return the value for *key* if *key* is in the underlying mapping, else
+      *default*.  If *default* is not given, it defaults to ``None``, so that
+      this method never raises a :exc:`KeyError`.
+
+   .. method:: items()
+
+      Return a new view of the underlying mapping's items (``(key, value)``
+      pairs).
+
+   .. method:: keys()
+
+      Return a new view of the underlying mapping's keys.
+
+   .. method:: values()
+
+      Return a new view of the underlying mapping's values.
+
+
diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst
index 766d3f3..495243f 100644
--- a/Doc/whatsnew/3.3.rst
+++ b/Doc/whatsnew/3.3.rst
@@ -1068,6 +1068,13 @@
 (Contributed by Victor Stinner in :issue:`10278`)
 
 
+types
+-----
+
+Add a new :class:`types.MappingProxyType` class: Read-only proxy of a mapping.
+(:issue:`14386`)
+
+
 urllib
 ------
 
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index 3051e57..c0c7414 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -4574,11 +4574,11 @@
         self.assertEqual(type(C.__dict__), type(B.__dict__))
 
     def test_repr(self):
-        # Testing dict_proxy.__repr__.
+        # Testing mappingproxy.__repr__.
         # We can't blindly compare with the repr of another dict as ordering
         # of keys and values is arbitrary and may differ.
         r = repr(self.C.__dict__)
-        self.assertTrue(r.startswith('dict_proxy('), r)
+        self.assertTrue(r.startswith('mappingproxy('), r)
         self.assertTrue(r.endswith(')'), r)
         for k, v in self.C.__dict__.items():
             self.assertIn('{!r}: {!r}'.format(k, v), r)
diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py
index 8a98a03..9a2e0d4 100644
--- a/Lib/test/test_types.py
+++ b/Lib/test/test_types.py
@@ -1,9 +1,11 @@
 # Python test set -- part 6, built-in types
 
 from test.support import run_unittest, run_with_locale
-import unittest
-import sys
+import collections
 import locale
+import sys
+import types
+import unittest
 
 class TypesTests(unittest.TestCase):
 
@@ -569,8 +571,184 @@
         self.assertGreater(tuple.__itemsize__, 0)
 
 
+class MappingProxyTests(unittest.TestCase):
+    mappingproxy = types.MappingProxyType
+
+    def test_constructor(self):
+        class userdict(dict):
+            pass
+
+        mapping = {'x': 1, 'y': 2}
+        self.assertEqual(self.mappingproxy(mapping), mapping)
+        mapping = userdict(x=1, y=2)
+        self.assertEqual(self.mappingproxy(mapping), mapping)
+        mapping = collections.ChainMap({'x': 1}, {'y': 2})
+        self.assertEqual(self.mappingproxy(mapping), mapping)
+
+        self.assertRaises(TypeError, self.mappingproxy, 10)
+        self.assertRaises(TypeError, self.mappingproxy, ("a", "tuple"))
+        self.assertRaises(TypeError, self.mappingproxy, ["a", "list"])
+
+    def test_methods(self):
+        attrs = set(dir(self.mappingproxy({}))) - set(dir(object()))
+        self.assertEqual(attrs, {
+             '__contains__',
+             '__getitem__',
+             '__iter__',
+             '__len__',
+             'copy',
+             'get',
+             'items',
+             'keys',
+             'values',
+        })
+
+    def test_get(self):
+        view = self.mappingproxy({'a': 'A', 'b': 'B'})
+        self.assertEqual(view['a'], 'A')
+        self.assertEqual(view['b'], 'B')
+        self.assertRaises(KeyError, view.__getitem__, 'xxx')
+        self.assertEqual(view.get('a'), 'A')
+        self.assertIsNone(view.get('xxx'))
+        self.assertEqual(view.get('xxx', 42), 42)
+
+    def test_missing(self):
+        class dictmissing(dict):
+            def __missing__(self, key):
+                return "missing=%s" % key
+
+        view = self.mappingproxy(dictmissing(x=1))
+        self.assertEqual(view['x'], 1)
+        self.assertEqual(view['y'], 'missing=y')
+        self.assertEqual(view.get('x'), 1)
+        self.assertEqual(view.get('y'), None)
+        self.assertEqual(view.get('y', 42), 42)
+        self.assertTrue('x' in view)
+        self.assertFalse('y' in view)
+
+    def test_customdict(self):
+        class customdict(dict):
+            def __contains__(self, key):
+                if key == 'magic':
+                    return True
+                else:
+                    return dict.__contains__(self, key)
+
+            def __iter__(self):
+                return iter(('iter',))
+
+            def __len__(self):
+                return 500
+
+            def copy(self):
+                return 'copy'
+
+            def keys(self):
+                return 'keys'
+
+            def items(self):
+                return 'items'
+
+            def values(self):
+                return 'values'
+
+            def __getitem__(self, key):
+                return "getitem=%s" % dict.__getitem__(self, key)
+
+            def get(self, key, default=None):
+                return "get=%s" % dict.get(self, key, 'default=%r' % default)
+
+        custom = customdict({'key': 'value'})
+        view = self.mappingproxy(custom)
+        self.assertTrue('key' in view)
+        self.assertTrue('magic' in view)
+        self.assertFalse('xxx' in view)
+        self.assertEqual(view['key'], 'getitem=value')
+        self.assertRaises(KeyError, view.__getitem__, 'xxx')
+        self.assertEqual(tuple(view), ('iter',))
+        self.assertEqual(len(view), 500)
+        self.assertEqual(view.copy(), 'copy')
+        self.assertEqual(view.get('key'), 'get=value')
+        self.assertEqual(view.get('xxx'), 'get=default=None')
+        self.assertEqual(view.items(), 'items')
+        self.assertEqual(view.keys(), 'keys')
+        self.assertEqual(view.values(), 'values')
+
+    def test_chainmap(self):
+        d1 = {'x': 1}
+        d2 = {'y': 2}
+        mapping = collections.ChainMap(d1, d2)
+        view = self.mappingproxy(mapping)
+        self.assertTrue('x' in view)
+        self.assertTrue('y' in view)
+        self.assertFalse('z' in view)
+        self.assertEqual(view['x'], 1)
+        self.assertEqual(view['y'], 2)
+        self.assertRaises(KeyError, view.__getitem__, 'z')
+        self.assertEqual(tuple(sorted(view)), ('x', 'y'))
+        self.assertEqual(len(view), 2)
+        copy = view.copy()
+        self.assertIsNot(copy, mapping)
+        self.assertIsInstance(copy, collections.ChainMap)
+        self.assertEqual(copy, mapping)
+        self.assertEqual(view.get('x'), 1)
+        self.assertEqual(view.get('y'), 2)
+        self.assertIsNone(view.get('z'))
+        self.assertEqual(tuple(sorted(view.items())), (('x', 1), ('y', 2)))
+        self.assertEqual(tuple(sorted(view.keys())), ('x', 'y'))
+        self.assertEqual(tuple(sorted(view.values())), (1, 2))
+
+    def test_contains(self):
+        view = self.mappingproxy(dict.fromkeys('abc'))
+        self.assertTrue('a' in view)
+        self.assertTrue('b' in view)
+        self.assertTrue('c' in view)
+        self.assertFalse('xxx' in view)
+
+    def test_views(self):
+        mapping = {}
+        view = self.mappingproxy(mapping)
+        keys = view.keys()
+        values = view.values()
+        items = view.items()
+        self.assertEqual(list(keys), [])
+        self.assertEqual(list(values), [])
+        self.assertEqual(list(items), [])
+        mapping['key'] = 'value'
+        self.assertEqual(list(keys), ['key'])
+        self.assertEqual(list(values), ['value'])
+        self.assertEqual(list(items), [('key', 'value')])
+
+    def test_len(self):
+        for expected in range(6):
+            data = dict.fromkeys('abcde'[:expected])
+            self.assertEqual(len(data), expected)
+            view = self.mappingproxy(data)
+            self.assertEqual(len(view), expected)
+
+    def test_iterators(self):
+        keys = ('x', 'y')
+        values = (1, 2)
+        items = tuple(zip(keys, values))
+        view = self.mappingproxy(dict(items))
+        self.assertEqual(set(view), set(keys))
+        self.assertEqual(set(view.keys()), set(keys))
+        self.assertEqual(set(view.values()), set(values))
+        self.assertEqual(set(view.items()), set(items))
+
+    def test_copy(self):
+        original = {'key1': 27, 'key2': 51, 'key3': 93}
+        view = self.mappingproxy(original)
+        copy = view.copy()
+        self.assertEqual(type(copy), dict)
+        self.assertEqual(copy, original)
+        original['key1'] = 70
+        self.assertEqual(view['key1'], 70)
+        self.assertEqual(copy['key1'], 27)
+
+
 def test_main():
-    run_unittest(TypesTests)
+    run_unittest(TypesTests, MappingProxyTests)
 
 if __name__ == '__main__':
     test_main()
diff --git a/Lib/types.py b/Lib/types.py
index ab354d1..08cbb83 100644
--- a/Lib/types.py
+++ b/Lib/types.py
@@ -12,6 +12,7 @@
 FunctionType = type(_f)
 LambdaType = type(lambda: None)         # Same as FunctionType
 CodeType = type(_f.__code__)
+MappingProxyType = type(type.__dict__)
 
 def _g():
     yield 1
diff --git a/Misc/NEWS b/Misc/NEWS
index e2e14b7..9e81cd8 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -32,6 +32,8 @@
 Library
 -------
 
+- Issue #14386: Expose the dict_proxy internal type as types.MappingProxyType.
+
 - Issue #13959: Make imp.reload() always use a module's __loader__ to perform
   the reload.
 
diff --git a/Objects/descrobject.c b/Objects/descrobject.c
index ecf372a..b679c4b 100644
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -698,41 +698,44 @@
 }
 
 
-/* --- Readonly proxy for dictionaries (actually any mapping) --- */
+/* --- mappingproxy: read-only proxy for mappings --- */
 
 /* This has no reason to be in this file except that adding new files is a
    bit of a pain */
 
 typedef struct {
     PyObject_HEAD
-    PyObject *dict;
-} proxyobject;
+    PyObject *mapping;
+} mappingproxyobject;
 
 static Py_ssize_t
-proxy_len(proxyobject *pp)
+mappingproxy_len(mappingproxyobject *pp)
 {
-    return PyObject_Size(pp->dict);
+    return PyObject_Size(pp->mapping);
 }
 
 static PyObject *
-proxy_getitem(proxyobject *pp, PyObject *key)
+mappingproxy_getitem(mappingproxyobject *pp, PyObject *key)
 {
-    return PyObject_GetItem(pp->dict, key);
+    return PyObject_GetItem(pp->mapping, key);
 }
 
-static PyMappingMethods proxy_as_mapping = {
-    (lenfunc)proxy_len,                         /* mp_length */
-    (binaryfunc)proxy_getitem,                  /* mp_subscript */
+static PyMappingMethods mappingproxy_as_mapping = {
+    (lenfunc)mappingproxy_len,                  /* mp_length */
+    (binaryfunc)mappingproxy_getitem,           /* mp_subscript */
     0,                                          /* mp_ass_subscript */
 };
 
 static int
-proxy_contains(proxyobject *pp, PyObject *key)
+mappingproxy_contains(mappingproxyobject *pp, PyObject *key)
 {
-    return PyDict_Contains(pp->dict, key);
+    if (PyDict_CheckExact(pp->mapping))
+        return PyDict_Contains(pp->mapping, key);
+    else
+        return PySequence_Contains(pp->mapping, key);
 }
 
-static PySequenceMethods proxy_as_sequence = {
+static PySequenceMethods mappingproxy_as_sequence = {
     0,                                          /* sq_length */
     0,                                          /* sq_concat */
     0,                                          /* sq_repeat */
@@ -740,152 +743,199 @@
     0,                                          /* sq_slice */
     0,                                          /* sq_ass_item */
     0,                                          /* sq_ass_slice */
-    (objobjproc)proxy_contains,                 /* sq_contains */
+    (objobjproc)mappingproxy_contains,                 /* sq_contains */
     0,                                          /* sq_inplace_concat */
     0,                                          /* sq_inplace_repeat */
 };
 
 static PyObject *
-proxy_get(proxyobject *pp, PyObject *args)
+mappingproxy_get(mappingproxyobject *pp, PyObject *args)
 {
     PyObject *key, *def = Py_None;
     _Py_IDENTIFIER(get);
 
     if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &def))
         return NULL;
-    return _PyObject_CallMethodId(pp->dict, &PyId_get, "(OO)", key, def);
+    return _PyObject_CallMethodId(pp->mapping, &PyId_get, "(OO)", key, def);
 }
 
 static PyObject *
-proxy_keys(proxyobject *pp)
+mappingproxy_keys(mappingproxyobject *pp)
 {
     _Py_IDENTIFIER(keys);
-    return _PyObject_CallMethodId(pp->dict, &PyId_keys, NULL);
+    return _PyObject_CallMethodId(pp->mapping, &PyId_keys, NULL);
 }
 
 static PyObject *
-proxy_values(proxyobject *pp)
+mappingproxy_values(mappingproxyobject *pp)
 {
     _Py_IDENTIFIER(values);
-    return _PyObject_CallMethodId(pp->dict, &PyId_values, NULL);
+    return _PyObject_CallMethodId(pp->mapping, &PyId_values, NULL);
 }
 
 static PyObject *
-proxy_items(proxyobject *pp)
+mappingproxy_items(mappingproxyobject *pp)
 {
     _Py_IDENTIFIER(items);
-    return _PyObject_CallMethodId(pp->dict, &PyId_items, NULL);
+    return _PyObject_CallMethodId(pp->mapping, &PyId_items, NULL);
 }
 
 static PyObject *
-proxy_copy(proxyobject *pp)
+mappingproxy_copy(mappingproxyobject *pp)
 {
     _Py_IDENTIFIER(copy);
-    return _PyObject_CallMethodId(pp->dict, &PyId_copy, NULL);
+    return _PyObject_CallMethodId(pp->mapping, &PyId_copy, NULL);
 }
 
-static PyMethodDef proxy_methods[] = {
-    {"get",       (PyCFunction)proxy_get,        METH_VARARGS,
+/* WARNING: mappingproxy methods must not give access
+            to the underlying mapping */
+
+static PyMethodDef mappingproxy_methods[] = {
+    {"get",       (PyCFunction)mappingproxy_get,        METH_VARARGS,
      PyDoc_STR("D.get(k[,d]) -> D[k] if k in D, else d."
-                                    "  d defaults to None.")},
-    {"keys",      (PyCFunction)proxy_keys,       METH_NOARGS,
+               "  d defaults to None.")},
+    {"keys",      (PyCFunction)mappingproxy_keys,       METH_NOARGS,
      PyDoc_STR("D.keys() -> list of D's keys")},
-    {"values",    (PyCFunction)proxy_values,     METH_NOARGS,
+    {"values",    (PyCFunction)mappingproxy_values,     METH_NOARGS,
      PyDoc_STR("D.values() -> list of D's values")},
-    {"items",     (PyCFunction)proxy_items,      METH_NOARGS,
+    {"items",     (PyCFunction)mappingproxy_items,      METH_NOARGS,
      PyDoc_STR("D.items() -> list of D's (key, value) pairs, as 2-tuples")},
-    {"copy",      (PyCFunction)proxy_copy,       METH_NOARGS,
+    {"copy",      (PyCFunction)mappingproxy_copy,       METH_NOARGS,
      PyDoc_STR("D.copy() -> a shallow copy of D")},
     {0}
 };
 
 static void
-proxy_dealloc(proxyobject *pp)
+mappingproxy_dealloc(mappingproxyobject *pp)
 {
     _PyObject_GC_UNTRACK(pp);
-    Py_DECREF(pp->dict);
+    Py_DECREF(pp->mapping);
     PyObject_GC_Del(pp);
 }
 
 static PyObject *
-proxy_getiter(proxyobject *pp)
+mappingproxy_getiter(mappingproxyobject *pp)
 {
-    return PyObject_GetIter(pp->dict);
+    return PyObject_GetIter(pp->mapping);
 }
 
 static PyObject *
-proxy_str(proxyobject *pp)
+mappingproxy_str(mappingproxyobject *pp)
 {
-    return PyObject_Str(pp->dict);
+    return PyObject_Str(pp->mapping);
 }
 
 static PyObject *
-proxy_repr(proxyobject *pp)
+mappingproxy_repr(mappingproxyobject *pp)
 {
-    return PyUnicode_FromFormat("dict_proxy(%R)", pp->dict);
+    return PyUnicode_FromFormat("mappingproxy(%R)", pp->mapping);
 }
 
 static int
-proxy_traverse(PyObject *self, visitproc visit, void *arg)
+mappingproxy_traverse(PyObject *self, visitproc visit, void *arg)
 {
-    proxyobject *pp = (proxyobject *)self;
-    Py_VISIT(pp->dict);
+    mappingproxyobject *pp = (mappingproxyobject *)self;
+    Py_VISIT(pp->mapping);
     return 0;
 }
 
 static PyObject *
-proxy_richcompare(proxyobject *v, PyObject *w, int op)
+mappingproxy_richcompare(mappingproxyobject *v, PyObject *w, int op)
 {
-    return PyObject_RichCompare(v->dict, w, op);
+    return PyObject_RichCompare(v->mapping, w, op);
+}
+
+static int
+mappingproxy_check_mapping(PyObject *mapping)
+{
+    if (!PyMapping_Check(mapping)
+        || PyList_Check(mapping)
+        || PyTuple_Check(mapping)) {
+        PyErr_Format(PyExc_TypeError,
+                    "mappingproxy() argument must be a mapping, not %s",
+                    Py_TYPE(mapping)->tp_name);
+        return -1;
+    }
+    return 0;
+}
+
+static PyObject*
+mappingproxy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    static char *kwlist[] = {"mapping", NULL};
+    PyObject *mapping;
+    mappingproxyobject *mappingproxy;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:mappingproxy",
+                                     kwlist, &mapping))
+        return NULL;
+
+    if (mappingproxy_check_mapping(mapping) == -1)
+        return NULL;
+
+    mappingproxy = PyObject_GC_New(mappingproxyobject, &PyDictProxy_Type);
+    if (mappingproxy == NULL)
+        return NULL;
+    Py_INCREF(mapping);
+    mappingproxy->mapping = mapping;
+    _PyObject_GC_TRACK(mappingproxy);
+    return (PyObject *)mappingproxy;
 }
 
 PyTypeObject PyDictProxy_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0)
-    "dict_proxy",                               /* tp_name */
-    sizeof(proxyobject),                        /* tp_basicsize */
+    "mappingproxy",                             /* tp_name */
+    sizeof(mappingproxyobject),                 /* tp_basicsize */
     0,                                          /* tp_itemsize */
     /* methods */
-    (destructor)proxy_dealloc,                  /* tp_dealloc */
+    (destructor)mappingproxy_dealloc,           /* tp_dealloc */
     0,                                          /* tp_print */
     0,                                          /* tp_getattr */
     0,                                          /* tp_setattr */
     0,                                          /* tp_reserved */
-    (reprfunc)proxy_repr,                       /* tp_repr */
+    (reprfunc)mappingproxy_repr,                /* tp_repr */
     0,                                          /* tp_as_number */
-    &proxy_as_sequence,                         /* tp_as_sequence */
-    &proxy_as_mapping,                          /* tp_as_mapping */
+    &mappingproxy_as_sequence,                  /* tp_as_sequence */
+    &mappingproxy_as_mapping,                   /* tp_as_mapping */
     0,                                          /* tp_hash */
     0,                                          /* tp_call */
-    (reprfunc)proxy_str,                        /* tp_str */
+    (reprfunc)mappingproxy_str,                 /* tp_str */
     PyObject_GenericGetAttr,                    /* tp_getattro */
     0,                                          /* tp_setattro */
     0,                                          /* tp_as_buffer */
     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
     0,                                          /* tp_doc */
-    proxy_traverse,                             /* tp_traverse */
+    mappingproxy_traverse,                      /* tp_traverse */
     0,                                          /* tp_clear */
-    (richcmpfunc)proxy_richcompare,             /* tp_richcompare */
+    (richcmpfunc)mappingproxy_richcompare,      /* tp_richcompare */
     0,                                          /* tp_weaklistoffset */
-    (getiterfunc)proxy_getiter,                 /* tp_iter */
+    (getiterfunc)mappingproxy_getiter,          /* tp_iter */
     0,                                          /* tp_iternext */
-    proxy_methods,                              /* tp_methods */
+    mappingproxy_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 */
+    0,                                          /* tp_alloc */
+    mappingproxy_new,                           /* tp_new */
 };
 
 PyObject *
-PyDictProxy_New(PyObject *dict)
+PyDictProxy_New(PyObject *mapping)
 {
-    proxyobject *pp;
+    mappingproxyobject *pp;
 
-    pp = PyObject_GC_New(proxyobject, &PyDictProxy_Type);
+    if (mappingproxy_check_mapping(mapping) == -1)
+        return NULL;
+
+    pp = PyObject_GC_New(mappingproxyobject, &PyDictProxy_Type);
     if (pp != NULL) {
-        Py_INCREF(dict);
-        pp->dict = dict;
+        Py_INCREF(mapping);
+        pp->mapping = mapping;
         _PyObject_GC_TRACK(pp);
     }
     return (PyObject *)pp;