Issue #14386: Expose the dict_proxy internal type as types.MappingProxyType
This commit is contained in:
parent
8a1d04c643
commit
0db176f8f6
|
@ -36,11 +36,11 @@ Dictionary Objects
|
||||||
Return a new empty dictionary, or *NULL* on failure.
|
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.
|
Return a :class:`types.MappingProxyType` object for a mapping which
|
||||||
This is normally used to create a proxy to prevent modification of the
|
enforces read-only behavior. This is normally used to create a view to
|
||||||
dictionary for non-dynamic class types.
|
prevent modification of the dictionary for non-dynamic class types.
|
||||||
|
|
||||||
|
|
||||||
.. c:function:: void PyDict_Clear(PyObject *p)
|
.. c:function:: void PyDict_Clear(PyObject *p)
|
||||||
|
|
|
@ -2258,13 +2258,13 @@ pairs within braces, for example: ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098:
|
||||||
|
|
||||||
.. method:: items()
|
.. method:: items()
|
||||||
|
|
||||||
Return a new view of the dictionary's items (``(key, value)`` pairs). See
|
Return a new view of the dictionary's items (``(key, value)`` pairs).
|
||||||
below for documentation of view objects.
|
See the :ref:`documentation of view objects <dict-views>`.
|
||||||
|
|
||||||
.. method:: keys()
|
.. method:: keys()
|
||||||
|
|
||||||
Return a new view of the dictionary's keys. See below for documentation of
|
Return a new view of the dictionary's keys. See the :ref:`documentation
|
||||||
view objects.
|
of view objects <dict-views>`.
|
||||||
|
|
||||||
.. method:: pop(key[, default])
|
.. method:: pop(key[, default])
|
||||||
|
|
||||||
|
@ -2298,8 +2298,12 @@ pairs within braces, for example: ``{'jack': 4098, 'sjoerd': 4127}`` or ``{4098:
|
||||||
|
|
||||||
.. method:: values()
|
.. method:: values()
|
||||||
|
|
||||||
Return a new view of the dictionary's values. See below for documentation of
|
Return a new view of the dictionary's values. See the
|
||||||
view objects.
|
: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:
|
.. _dict-views:
|
||||||
|
|
|
@ -85,3 +85,55 @@ The module defines the following names:
|
||||||
|
|
||||||
In other implementations of Python, this type may be identical to
|
In other implementations of Python, this type may be identical to
|
||||||
``GetSetDescriptorType``.
|
``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.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1068,6 +1068,13 @@ The :mod:`time` module has new functions:
|
||||||
(Contributed by Victor Stinner in :issue:`10278`)
|
(Contributed by Victor Stinner in :issue:`10278`)
|
||||||
|
|
||||||
|
|
||||||
|
types
|
||||||
|
-----
|
||||||
|
|
||||||
|
Add a new :class:`types.MappingProxyType` class: Read-only proxy of a mapping.
|
||||||
|
(:issue:`14386`)
|
||||||
|
|
||||||
|
|
||||||
urllib
|
urllib
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|
|
@ -4574,11 +4574,11 @@ class DictProxyTests(unittest.TestCase):
|
||||||
self.assertEqual(type(C.__dict__), type(B.__dict__))
|
self.assertEqual(type(C.__dict__), type(B.__dict__))
|
||||||
|
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
# Testing dict_proxy.__repr__.
|
# Testing mappingproxy.__repr__.
|
||||||
# We can't blindly compare with the repr of another dict as ordering
|
# We can't blindly compare with the repr of another dict as ordering
|
||||||
# of keys and values is arbitrary and may differ.
|
# of keys and values is arbitrary and may differ.
|
||||||
r = repr(self.C.__dict__)
|
r = repr(self.C.__dict__)
|
||||||
self.assertTrue(r.startswith('dict_proxy('), r)
|
self.assertTrue(r.startswith('mappingproxy('), r)
|
||||||
self.assertTrue(r.endswith(')'), r)
|
self.assertTrue(r.endswith(')'), r)
|
||||||
for k, v in self.C.__dict__.items():
|
for k, v in self.C.__dict__.items():
|
||||||
self.assertIn('{!r}: {!r}'.format(k, v), r)
|
self.assertIn('{!r}: {!r}'.format(k, v), r)
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
# Python test set -- part 6, built-in types
|
# Python test set -- part 6, built-in types
|
||||||
|
|
||||||
from test.support import run_unittest, run_with_locale
|
from test.support import run_unittest, run_with_locale
|
||||||
import unittest
|
import collections
|
||||||
import sys
|
|
||||||
import locale
|
import locale
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
import unittest
|
||||||
|
|
||||||
class TypesTests(unittest.TestCase):
|
class TypesTests(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -569,8 +571,184 @@ class TypesTests(unittest.TestCase):
|
||||||
self.assertGreater(tuple.__itemsize__, 0)
|
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():
|
def test_main():
|
||||||
run_unittest(TypesTests)
|
run_unittest(TypesTests, MappingProxyTests)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
test_main()
|
test_main()
|
||||||
|
|
|
@ -12,6 +12,7 @@ def _f(): pass
|
||||||
FunctionType = type(_f)
|
FunctionType = type(_f)
|
||||||
LambdaType = type(lambda: None) # Same as FunctionType
|
LambdaType = type(lambda: None) # Same as FunctionType
|
||||||
CodeType = type(_f.__code__)
|
CodeType = type(_f.__code__)
|
||||||
|
MappingProxyType = type(type.__dict__)
|
||||||
|
|
||||||
def _g():
|
def _g():
|
||||||
yield 1
|
yield 1
|
||||||
|
|
|
@ -32,6 +32,8 @@ Core and Builtins
|
||||||
Library
|
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
|
- Issue #13959: Make imp.reload() always use a module's __loader__ to perform
|
||||||
the reload.
|
the reload.
|
||||||
|
|
||||||
|
|
|
@ -698,41 +698,44 @@ PyDescr_NewWrapper(PyTypeObject *type, struct wrapperbase *base, void *wrapped)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --- 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
|
/* This has no reason to be in this file except that adding new files is a
|
||||||
bit of a pain */
|
bit of a pain */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
PyObject *dict;
|
PyObject *mapping;
|
||||||
} proxyobject;
|
} mappingproxyobject;
|
||||||
|
|
||||||
static Py_ssize_t
|
static Py_ssize_t
|
||||||
proxy_len(proxyobject *pp)
|
mappingproxy_len(mappingproxyobject *pp)
|
||||||
{
|
{
|
||||||
return PyObject_Size(pp->dict);
|
return PyObject_Size(pp->mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
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 = {
|
static PyMappingMethods mappingproxy_as_mapping = {
|
||||||
(lenfunc)proxy_len, /* mp_length */
|
(lenfunc)mappingproxy_len, /* mp_length */
|
||||||
(binaryfunc)proxy_getitem, /* mp_subscript */
|
(binaryfunc)mappingproxy_getitem, /* mp_subscript */
|
||||||
0, /* mp_ass_subscript */
|
0, /* mp_ass_subscript */
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
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_length */
|
||||||
0, /* sq_concat */
|
0, /* sq_concat */
|
||||||
0, /* sq_repeat */
|
0, /* sq_repeat */
|
||||||
|
@ -740,152 +743,199 @@ static PySequenceMethods proxy_as_sequence = {
|
||||||
0, /* sq_slice */
|
0, /* sq_slice */
|
||||||
0, /* sq_ass_item */
|
0, /* sq_ass_item */
|
||||||
0, /* sq_ass_slice */
|
0, /* sq_ass_slice */
|
||||||
(objobjproc)proxy_contains, /* sq_contains */
|
(objobjproc)mappingproxy_contains, /* sq_contains */
|
||||||
0, /* sq_inplace_concat */
|
0, /* sq_inplace_concat */
|
||||||
0, /* sq_inplace_repeat */
|
0, /* sq_inplace_repeat */
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
proxy_get(proxyobject *pp, PyObject *args)
|
mappingproxy_get(mappingproxyobject *pp, PyObject *args)
|
||||||
{
|
{
|
||||||
PyObject *key, *def = Py_None;
|
PyObject *key, *def = Py_None;
|
||||||
_Py_IDENTIFIER(get);
|
_Py_IDENTIFIER(get);
|
||||||
|
|
||||||
if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &def))
|
if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &def))
|
||||||
return NULL;
|
return NULL;
|
||||||
return _PyObject_CallMethodId(pp->dict, &PyId_get, "(OO)", key, def);
|
return _PyObject_CallMethodId(pp->mapping, &PyId_get, "(OO)", key, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
proxy_keys(proxyobject *pp)
|
mappingproxy_keys(mappingproxyobject *pp)
|
||||||
{
|
{
|
||||||
_Py_IDENTIFIER(keys);
|
_Py_IDENTIFIER(keys);
|
||||||
return _PyObject_CallMethodId(pp->dict, &PyId_keys, NULL);
|
return _PyObject_CallMethodId(pp->mapping, &PyId_keys, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
proxy_values(proxyobject *pp)
|
mappingproxy_values(mappingproxyobject *pp)
|
||||||
{
|
{
|
||||||
_Py_IDENTIFIER(values);
|
_Py_IDENTIFIER(values);
|
||||||
return _PyObject_CallMethodId(pp->dict, &PyId_values, NULL);
|
return _PyObject_CallMethodId(pp->mapping, &PyId_values, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
proxy_items(proxyobject *pp)
|
mappingproxy_items(mappingproxyobject *pp)
|
||||||
{
|
{
|
||||||
_Py_IDENTIFIER(items);
|
_Py_IDENTIFIER(items);
|
||||||
return _PyObject_CallMethodId(pp->dict, &PyId_items, NULL);
|
return _PyObject_CallMethodId(pp->mapping, &PyId_items, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
proxy_copy(proxyobject *pp)
|
mappingproxy_copy(mappingproxyobject *pp)
|
||||||
{
|
{
|
||||||
_Py_IDENTIFIER(copy);
|
_Py_IDENTIFIER(copy);
|
||||||
return _PyObject_CallMethodId(pp->dict, &PyId_copy, NULL);
|
return _PyObject_CallMethodId(pp->mapping, &PyId_copy, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyMethodDef proxy_methods[] = {
|
/* WARNING: mappingproxy methods must not give access
|
||||||
{"get", (PyCFunction)proxy_get, METH_VARARGS,
|
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."
|
PyDoc_STR("D.get(k[,d]) -> D[k] if k in D, else d."
|
||||||
" d defaults to None.")},
|
" d defaults to None.")},
|
||||||
{"keys", (PyCFunction)proxy_keys, METH_NOARGS,
|
{"keys", (PyCFunction)mappingproxy_keys, METH_NOARGS,
|
||||||
PyDoc_STR("D.keys() -> list of D's keys")},
|
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")},
|
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")},
|
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")},
|
PyDoc_STR("D.copy() -> a shallow copy of D")},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
proxy_dealloc(proxyobject *pp)
|
mappingproxy_dealloc(mappingproxyobject *pp)
|
||||||
{
|
{
|
||||||
_PyObject_GC_UNTRACK(pp);
|
_PyObject_GC_UNTRACK(pp);
|
||||||
Py_DECREF(pp->dict);
|
Py_DECREF(pp->mapping);
|
||||||
PyObject_GC_Del(pp);
|
PyObject_GC_Del(pp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
proxy_getiter(proxyobject *pp)
|
mappingproxy_getiter(mappingproxyobject *pp)
|
||||||
{
|
{
|
||||||
return PyObject_GetIter(pp->dict);
|
return PyObject_GetIter(pp->mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
proxy_str(proxyobject *pp)
|
mappingproxy_str(mappingproxyobject *pp)
|
||||||
{
|
{
|
||||||
return PyObject_Str(pp->dict);
|
return PyObject_Str(pp->mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
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
|
static int
|
||||||
proxy_traverse(PyObject *self, visitproc visit, void *arg)
|
mappingproxy_traverse(PyObject *self, visitproc visit, void *arg)
|
||||||
{
|
{
|
||||||
proxyobject *pp = (proxyobject *)self;
|
mappingproxyobject *pp = (mappingproxyobject *)self;
|
||||||
Py_VISIT(pp->dict);
|
Py_VISIT(pp->mapping);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
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 = {
|
PyTypeObject PyDictProxy_Type = {
|
||||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||||
"dict_proxy", /* tp_name */
|
"mappingproxy", /* tp_name */
|
||||||
sizeof(proxyobject), /* tp_basicsize */
|
sizeof(mappingproxyobject), /* tp_basicsize */
|
||||||
0, /* tp_itemsize */
|
0, /* tp_itemsize */
|
||||||
/* methods */
|
/* methods */
|
||||||
(destructor)proxy_dealloc, /* tp_dealloc */
|
(destructor)mappingproxy_dealloc, /* tp_dealloc */
|
||||||
0, /* tp_print */
|
0, /* tp_print */
|
||||||
0, /* tp_getattr */
|
0, /* tp_getattr */
|
||||||
0, /* tp_setattr */
|
0, /* tp_setattr */
|
||||||
0, /* tp_reserved */
|
0, /* tp_reserved */
|
||||||
(reprfunc)proxy_repr, /* tp_repr */
|
(reprfunc)mappingproxy_repr, /* tp_repr */
|
||||||
0, /* tp_as_number */
|
0, /* tp_as_number */
|
||||||
&proxy_as_sequence, /* tp_as_sequence */
|
&mappingproxy_as_sequence, /* tp_as_sequence */
|
||||||
&proxy_as_mapping, /* tp_as_mapping */
|
&mappingproxy_as_mapping, /* tp_as_mapping */
|
||||||
0, /* tp_hash */
|
0, /* tp_hash */
|
||||||
0, /* tp_call */
|
0, /* tp_call */
|
||||||
(reprfunc)proxy_str, /* tp_str */
|
(reprfunc)mappingproxy_str, /* tp_str */
|
||||||
PyObject_GenericGetAttr, /* tp_getattro */
|
PyObject_GenericGetAttr, /* tp_getattro */
|
||||||
0, /* tp_setattro */
|
0, /* tp_setattro */
|
||||||
0, /* tp_as_buffer */
|
0, /* tp_as_buffer */
|
||||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
||||||
0, /* tp_doc */
|
0, /* tp_doc */
|
||||||
proxy_traverse, /* tp_traverse */
|
mappingproxy_traverse, /* tp_traverse */
|
||||||
0, /* tp_clear */
|
0, /* tp_clear */
|
||||||
(richcmpfunc)proxy_richcompare, /* tp_richcompare */
|
(richcmpfunc)mappingproxy_richcompare, /* tp_richcompare */
|
||||||
0, /* tp_weaklistoffset */
|
0, /* tp_weaklistoffset */
|
||||||
(getiterfunc)proxy_getiter, /* tp_iter */
|
(getiterfunc)mappingproxy_getiter, /* tp_iter */
|
||||||
0, /* tp_iternext */
|
0, /* tp_iternext */
|
||||||
proxy_methods, /* tp_methods */
|
mappingproxy_methods, /* tp_methods */
|
||||||
0, /* tp_members */
|
0, /* tp_members */
|
||||||
0, /* tp_getset */
|
0, /* tp_getset */
|
||||||
0, /* tp_base */
|
0, /* tp_base */
|
||||||
0, /* tp_dict */
|
0, /* tp_dict */
|
||||||
0, /* tp_descr_get */
|
0, /* tp_descr_get */
|
||||||
0, /* tp_descr_set */
|
0, /* tp_descr_set */
|
||||||
|
0, /* tp_dictoffset */
|
||||||
|
0, /* tp_init */
|
||||||
|
0, /* tp_alloc */
|
||||||
|
mappingproxy_new, /* tp_new */
|
||||||
};
|
};
|
||||||
|
|
||||||
PyObject *
|
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) {
|
if (pp != NULL) {
|
||||||
Py_INCREF(dict);
|
Py_INCREF(mapping);
|
||||||
pp->dict = dict;
|
pp->mapping = mapping;
|
||||||
_PyObject_GC_TRACK(pp);
|
_PyObject_GC_TRACK(pp);
|
||||||
}
|
}
|
||||||
return (PyObject *)pp;
|
return (PyObject *)pp;
|
||||||
|
|
Loading…
Reference in New Issue