diff -r 51016ff7f8c9 Doc/c-api/dict.rst --- a/Doc/c-api/dict.rst Sun Mar 25 22:41:16 2012 -0400 +++ b/Doc/c-api/dict.rst Mon Mar 26 13:55:47 2012 +0200 @@ -36,11 +36,11 @@ Dictionary Objects 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:`collections.mappingview` 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 -r 51016ff7f8c9 Doc/library/collections.rst --- a/Doc/library/collections.rst Sun Mar 25 22:41:16 2012 -0400 +++ b/Doc/library/collections.rst Mon Mar 26 13:55:47 2012 +0200 @@ -23,6 +23,7 @@ Python's general purpose built-in contai ===================== ==================================================================== :func:`namedtuple` factory function for creating tuple subclasses with named fields :class:`deque` list-like container with fast appends and pops on either end +:class:`mappingview` read-only view of a mapping :class:`ChainMap` dict-like class for creating a single view of multiple mappings :class:`Counter` dict subclass for counting hashable objects :class:`OrderedDict` dict subclass that remembers the order entries were added @@ -995,6 +996,62 @@ so that the counter remembers the order return self.__class__, (OrderedDict(self),) +:class:`mappingview` objects +---------------------------- + +.. class:: mappingview(mapping) + + Read-only view 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 mappingview + + Return ``True`` if the underlying mapping has a key *key*, else + ``False``. + + .. describe:: mappingview[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(mappingview) + + Return an iterator over the keys of the underlying mapping. This is a + shortcut for ``iter(mappingview.keys())``. + + .. describe:: len(mappingview) + + Return the number of items in the underlying mapping. + + .. method:: copy() + + Return a shallow copy of the underlying mapping as a :class:`dict`. + + .. 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). See the :ref:`documentation of view objects `. + + .. method:: keys() + + Return a new view of the underlying mapping's keys. See the + :ref:`documentation of view objects `. + + .. method:: values() + + Return a new view of the underlying mapping's values. See the + :ref:`documentation of view objects `. + + :class:`UserDict` objects ------------------------- diff -r 51016ff7f8c9 Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst Sun Mar 25 22:41:16 2012 -0400 +++ b/Doc/library/stdtypes.rst Mon Mar 26 13:55:47 2012 +0200 @@ -2258,13 +2258,13 @@ pairs within braces, for example: ``{'ja .. 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 `. .. 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 `. .. method:: pop(key[, default]) @@ -2298,8 +2298,8 @@ pairs within braces, for example: ``{'ja .. 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: @@ -2379,6 +2379,10 @@ An example of dictionary view usage:: >>> keys ^ {'sausage', 'juice'} {'juice', 'sausage', 'bacon', 'spam'} +.. seealso:: + :class:`collections.mappingview` can be used to create a read-only view + of a :class:`dict`. + .. _typememoryview: diff -r 51016ff7f8c9 Include/Python.h --- a/Include/Python.h Sun Mar 25 22:41:16 2012 -0400 +++ b/Include/Python.h Mon Mar 26 13:55:47 2012 +0200 @@ -101,6 +101,7 @@ #include "warnings.h" #include "weakrefobject.h" #include "structseq.h" +#include "mappingviewobject.h" #include "codecs.h" #include "pyerrors.h" diff -r 51016ff7f8c9 Include/descrobject.h --- a/Include/descrobject.h Sun Mar 25 22:41:16 2012 -0400 +++ b/Include/descrobject.h Mon Mar 26 13:55:47 2012 +0200 @@ -77,6 +77,7 @@ PyAPI_DATA(PyTypeObject) PyGetSetDescr_T PyAPI_DATA(PyTypeObject) PyMemberDescr_Type; PyAPI_DATA(PyTypeObject) PyMethodDescr_Type; PyAPI_DATA(PyTypeObject) PyWrapperDescr_Type; +/* PyDictProxy_Type is defined in _collectionsmodule.c */ PyAPI_DATA(PyTypeObject) PyDictProxy_Type; PyAPI_DATA(PyTypeObject) _PyMethodWrapper_Type; @@ -93,6 +94,7 @@ PyAPI_FUNC(PyObject *) PyDescr_NewWrappe #define PyDescr_IsData(d) (Py_TYPE(d)->tp_descr_set != NULL) #endif +/* PyDictProxy_New() is defined in _collectionsmodule.c */ PyAPI_FUNC(PyObject *) PyDictProxy_New(PyObject *); PyAPI_FUNC(PyObject *) PyWrapper_New(PyObject *, PyObject *); diff -r 51016ff7f8c9 Lib/collections/__init__.py --- a/Lib/collections/__init__.py Sun Mar 25 22:41:16 2012 -0400 +++ b/Lib/collections/__init__.py Mon Mar 26 13:55:47 2012 +0200 @@ -1,5 +1,5 @@ __all__ = ['deque', 'defaultdict', 'namedtuple', 'UserDict', 'UserList', - 'UserString', 'Counter', 'OrderedDict', 'ChainMap'] + 'UserString', 'Counter', 'OrderedDict', 'ChainMap', 'mappingview'] # For backwards compatibility, continue to make the collections ABCs # available through the collections module. @@ -7,7 +7,7 @@ from collections.abc import * import collections.abc __all__ += collections.abc.__all__ -from _collections import deque, defaultdict +from _collections import deque, defaultdict, mappingview from operator import itemgetter as _itemgetter from keyword import iskeyword as _iskeyword import sys as _sys diff -r 51016ff7f8c9 Lib/test/test_collections.py --- a/Lib/test/test_collections.py Sun Mar 25 22:41:16 2012 -0400 +++ b/Lib/test/test_collections.py Mon Mar 26 13:55:47 2012 +0200 @@ -4,7 +4,7 @@ import unittest, doctest, operator from test.support import TESTFN, forget, unlink import inspect from test import support -from collections import namedtuple, Counter, OrderedDict, _count_elements +from collections import namedtuple, Counter, OrderedDict, _count_elements, mappingview from test import mapping_tests import pickle, copy from random import randrange, shuffle @@ -1271,6 +1271,152 @@ class SubclassMappingTests(mapping_tests self.assertRaises(KeyError, d.popitem) +class MappingViewTests(unittest.TestCase): + def test_constructor(self): + class userdict(dict): + pass + + d = {'x': 1, 'y': 2} + self.assertEqual(mappingview(d), d) + d = userdict(x=1, y=2) + self.assertEqual(mappingview(d), d) + self.assertRaises(TypeError, mappingview, 10) + self.assertRaises(TypeError, mappingview, ("a", "tuple")) + self.assertRaises(TypeError, mappingview, ["a", "list"]) + self.assertRaises(TypeError, mappingview, collections.ChainMap({})) + + def test_methods(self): + attrs = set(dir(mappingview({}))) - set(dir(object())) + self.assertEqual(attrs, { + '__contains__', + '__getitem__', + '__iter__', + '__len__', + 'copy', + 'get', + 'items', + 'keys', + 'values', + }) + + def test_get(self): + view = mappingview({'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 = mappingview(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) + + custom = customdict({'key': 'value'}) + view = mappingview(custom) + self.assertTrue('key' in view) + self.assertTrue('magic' in view) + self.assertFalse('xxx' in view) + self.assertEqual(view['key'], 'getitem=value') + 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.items(), 'items') + self.assertEqual(view.keys(), 'keys') + self.assertEqual(view.values(), 'values') + + def test_contains(self): + view = mappingview(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 = mappingview(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 = mappingview(data) + self.assertEqual(len(view), expected) + + def test_iterators(self): + keys = ('x', 'y') + values = (1, 2) + items = tuple(zip(keys, values)) + view = mappingview(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 = mappingview(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) + + ################################################################################ ### Run tests ################################################################################ @@ -1281,7 +1427,8 @@ def test_main(verbose=None): NamedTupleDocs = doctest.DocTestSuite(module=collections) test_classes = [TestNamedTuple, NamedTupleDocs, TestOneTrickPonyABCs, TestCollectionABCs, TestCounter, TestChainMap, - TestOrderedDict, GeneralMappingTests, SubclassMappingTests] + TestOrderedDict, GeneralMappingTests, SubclassMappingTests, + MappingViewTests] support.run_unittest(*test_classes) support.run_doctest(collections, verbose) diff -r 51016ff7f8c9 Lib/test/test_descr.py --- a/Lib/test/test_descr.py Sun Mar 25 22:41:16 2012 -0400 +++ b/Lib/test/test_descr.py Mon Mar 26 13:55:47 2012 +0200 @@ -4574,11 +4574,11 @@ class DictProxyTests(unittest.TestCase): self.assertEqual(type(C.__dict__), type(B.__dict__)) def test_repr(self): - # Testing dict_proxy.__repr__. + # Testing mappingview.__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('collections.mappingview('), r) self.assertTrue(r.endswith(')'), r) for k, v in self.C.__dict__.items(): self.assertIn('{!r}: {!r}'.format(k, v), r) diff -r 51016ff7f8c9 Modules/_collectionsmodule.c --- a/Modules/_collectionsmodule.c Sun Mar 25 22:41:16 2012 -0400 +++ b/Modules/_collectionsmodule.c Mon Mar 26 13:55:47 2012 +0200 @@ -1525,6 +1525,242 @@ static PyTypeObject defdict_type = { PyObject_GC_Del, /* tp_free */ }; +/* Read-only view for mappings: mappingview type ****************************/ + +typedef struct { + PyObject_HEAD + PyObject *dict; +} mappingviewobject; + +static Py_ssize_t +mappingview_len(mappingviewobject *pp) +{ + return PyObject_Size(pp->dict); +} + +static PyObject * +mappingview_getitem(mappingviewobject *pp, PyObject *key) +{ + return PyObject_GetItem(pp->dict, key); +} + +static PyMappingMethods mappingview_as_mapping = { + (lenfunc)mappingview_len, /* mp_length */ + (binaryfunc)mappingview_getitem, /* mp_subscript */ + 0, /* mp_ass_subscript */ +}; + +static int +mappingview_contains(mappingviewobject *pp, PyObject *key) +{ + if (PyDict_CheckExact(pp->dict)) + return PyDict_Contains(pp->dict, key); + else + return PySequence_Contains(pp->dict, key); +} + +static PySequenceMethods mappingview_as_sequence = { + 0, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + 0, /* sq_item */ + 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)mappingview_contains, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static PyObject * +mappingview_get(mappingviewobject *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); +} + +static PyObject * +mappingview_keys(mappingviewobject *pp) +{ + _Py_IDENTIFIER(keys); + return _PyObject_CallMethodId(pp->dict, &PyId_keys, NULL); +} + +static PyObject * +mappingview_values(mappingviewobject *pp) +{ + _Py_IDENTIFIER(values); + return _PyObject_CallMethodId(pp->dict, &PyId_values, NULL); +} + +static PyObject * +mappingview_items(mappingviewobject *pp) +{ + _Py_IDENTIFIER(items); + return _PyObject_CallMethodId(pp->dict, &PyId_items, NULL); +} + +static PyObject * +mappingview_copy(mappingviewobject *pp) +{ + _Py_IDENTIFIER(copy); + return _PyObject_CallMethodId(pp->dict, &PyId_copy, NULL); +} + +static PyMethodDef mappingview_methods[] = { + {"get", (PyCFunction)mappingview_get, METH_VARARGS, + PyDoc_STR("D.get(k[,d]) -> D[k] if k in D, else d." + " d defaults to None.")}, + {"keys", (PyCFunction)mappingview_keys, METH_NOARGS, + PyDoc_STR("D.keys() -> list of D's keys")}, + {"values", (PyCFunction)mappingview_values, METH_NOARGS, + PyDoc_STR("D.values() -> list of D's values")}, + {"items", (PyCFunction)mappingview_items, METH_NOARGS, + PyDoc_STR("D.items() -> list of D's (key, value) pairs, as 2-tuples")}, + {"copy", (PyCFunction)mappingview_copy, METH_NOARGS, + PyDoc_STR("D.copy() -> a shallow copy of D")}, + {0} +}; + +static void +mappingview_dealloc(mappingviewobject *pp) +{ + _PyObject_GC_UNTRACK(pp); + Py_DECREF(pp->dict); + PyObject_GC_Del(pp); +} + +static PyObject * +mappingview_getiter(mappingviewobject *pp) +{ + return PyObject_GetIter(pp->dict); +} + +static PyObject * +mappingview_str(mappingviewobject *pp) +{ + return PyObject_Str(pp->dict); +} + +static PyObject * +mappingview_repr(mappingviewobject *pp) +{ + return PyUnicode_FromFormat("collections.mappingview(%R)", pp->dict); +} + +static int +mappingview_traverse(PyObject *self, visitproc visit, void *arg) +{ + mappingviewobject *pp = (mappingviewobject *)self; + Py_VISIT(pp->dict); + return 0; +} + +static PyObject * +mappingview_richcompare(mappingviewobject *v, PyObject *w, int op) +{ + return PyObject_RichCompare(v->dict, w, op); +} + +static int +mappingview_check_dict(PyObject *dict) +{ + if (!PyDict_Check(dict)) { + PyErr_Format(PyExc_TypeError, + "mappingview() argument must be a mapping, not %s", + Py_TYPE(dict)->tp_name); + return -1; + } + return 0; +} + +static PyObject* +mappingview_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"dict", 0}; + PyObject *dict; + mappingviewobject *mappingview; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:mappingview", + kwlist, &dict)) + return NULL; + + if (mappingview_check_dict(dict) == -1) + return NULL; + + mappingview = PyObject_GC_New(mappingviewobject, &PyDictProxy_Type); + if (mappingview == NULL) + return NULL; + Py_INCREF(dict); + mappingview->dict = dict; + _PyObject_GC_TRACK(mappingview); + return (PyObject *)mappingview; +} + +PyTypeObject PyDictProxy_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "collections.mappingview", /* tp_name */ + sizeof(mappingviewobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)mappingview_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + (reprfunc)mappingview_repr, /* tp_repr */ + 0, /* tp_as_number */ + &mappingview_as_sequence, /* tp_as_sequence */ + &mappingview_as_mapping, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc)mappingview_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 */ + mappingview_traverse, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)mappingview_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + (getiterfunc)mappingview_getiter, /* tp_iter */ + 0, /* tp_iternext */ + mappingview_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 */ + mappingview_new, /* tp_new */ +}; + +PyObject * +PyDictProxy_New(PyObject *dict) +{ + mappingviewobject *pp; + + if (mappingview_check_dict(dict) == -1) + return NULL; + + pp = PyObject_GC_New(mappingviewobject, &PyDictProxy_Type); + if (pp != NULL) { + Py_INCREF(dict); + pp->dict = dict; + _PyObject_GC_TRACK(pp); + } + return (PyObject *)pp; +} + + /* helper function for Counter *********************************************/ PyDoc_STRVAR(_count_elements_doc, @@ -1657,5 +1893,7 @@ PyInit__collections(void) if (PyType_Ready(&dequereviter_type) < 0) return NULL; + PyModule_AddObject(m, "mappingview", (PyObject *)&PyDictProxy_Type); + return m; } diff -r 51016ff7f8c9 Objects/descrobject.c --- a/Objects/descrobject.c Sun Mar 25 22:41:16 2012 -0400 +++ b/Objects/descrobject.c Mon Mar 26 13:55:47 2012 +0200 @@ -698,200 +698,6 @@ PyDescr_NewWrapper(PyTypeObject *type, s } -/* --- Readonly proxy for dictionaries (actually any mapping) --- */ - -/* 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; - -static Py_ssize_t -proxy_len(proxyobject *pp) -{ - return PyObject_Size(pp->dict); -} - -static PyObject * -proxy_getitem(proxyobject *pp, PyObject *key) -{ - return PyObject_GetItem(pp->dict, key); -} - -static PyMappingMethods proxy_as_mapping = { - (lenfunc)proxy_len, /* mp_length */ - (binaryfunc)proxy_getitem, /* mp_subscript */ - 0, /* mp_ass_subscript */ -}; - -static int -proxy_contains(proxyobject *pp, PyObject *key) -{ - return PyDict_Contains(pp->dict, key); -} - -static PySequenceMethods proxy_as_sequence = { - 0, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - 0, /* sq_item */ - 0, /* sq_slice */ - 0, /* sq_ass_item */ - 0, /* sq_ass_slice */ - (objobjproc)proxy_contains, /* sq_contains */ - 0, /* sq_inplace_concat */ - 0, /* sq_inplace_repeat */ -}; - -static PyObject * -proxy_get(proxyobject *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); -} - -static PyObject * -proxy_keys(proxyobject *pp) -{ - _Py_IDENTIFIER(keys); - return _PyObject_CallMethodId(pp->dict, &PyId_keys, NULL); -} - -static PyObject * -proxy_values(proxyobject *pp) -{ - _Py_IDENTIFIER(values); - return _PyObject_CallMethodId(pp->dict, &PyId_values, NULL); -} - -static PyObject * -proxy_items(proxyobject *pp) -{ - _Py_IDENTIFIER(items); - return _PyObject_CallMethodId(pp->dict, &PyId_items, NULL); -} - -static PyObject * -proxy_copy(proxyobject *pp) -{ - _Py_IDENTIFIER(copy); - return _PyObject_CallMethodId(pp->dict, &PyId_copy, NULL); -} - -static PyMethodDef proxy_methods[] = { - {"get", (PyCFunction)proxy_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, - PyDoc_STR("D.keys() -> list of D's keys")}, - {"values", (PyCFunction)proxy_values, METH_NOARGS, - PyDoc_STR("D.values() -> list of D's values")}, - {"items", (PyCFunction)proxy_items, METH_NOARGS, - PyDoc_STR("D.items() -> list of D's (key, value) pairs, as 2-tuples")}, - {"copy", (PyCFunction)proxy_copy, METH_NOARGS, - PyDoc_STR("D.copy() -> a shallow copy of D")}, - {0} -}; - -static void -proxy_dealloc(proxyobject *pp) -{ - _PyObject_GC_UNTRACK(pp); - Py_DECREF(pp->dict); - PyObject_GC_Del(pp); -} - -static PyObject * -proxy_getiter(proxyobject *pp) -{ - return PyObject_GetIter(pp->dict); -} - -static PyObject * -proxy_str(proxyobject *pp) -{ - return PyObject_Str(pp->dict); -} - -static PyObject * -proxy_repr(proxyobject *pp) -{ - return PyUnicode_FromFormat("dict_proxy(%R)", pp->dict); -} - -static int -proxy_traverse(PyObject *self, visitproc visit, void *arg) -{ - proxyobject *pp = (proxyobject *)self; - Py_VISIT(pp->dict); - return 0; -} - -static PyObject * -proxy_richcompare(proxyobject *v, PyObject *w, int op) -{ - return PyObject_RichCompare(v->dict, w, op); -} - -PyTypeObject PyDictProxy_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "dict_proxy", /* tp_name */ - sizeof(proxyobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)proxy_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_reserved */ - (reprfunc)proxy_repr, /* tp_repr */ - 0, /* tp_as_number */ - &proxy_as_sequence, /* tp_as_sequence */ - &proxy_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - (reprfunc)proxy_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 */ - 0, /* tp_clear */ - (richcmpfunc)proxy_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)proxy_getiter, /* tp_iter */ - 0, /* tp_iternext */ - proxy_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ -}; - -PyObject * -PyDictProxy_New(PyObject *dict) -{ - proxyobject *pp; - - pp = PyObject_GC_New(proxyobject, &PyDictProxy_Type); - if (pp != NULL) { - Py_INCREF(dict); - pp->dict = dict; - _PyObject_GC_TRACK(pp); - } - return (PyObject *)pp; -} - - /* --- Wrapper object for "slot" methods --- */ /* This has no reason to be in this file except that adding new files is a