bpo-36144: Update MappingProxyType with PEP 584's operators (#18814)

We make `|=` raise TypeError, since it would be surprising if `C.__dict__ |= {'x': 0}` silently did nothing, while `C.__dict__.update({'x': 0})` is an error.
This commit is contained in:
Brandt Bucher 2020-03-07 11:03:09 -08:00 committed by GitHub
parent 8f13053692
commit 4663f66f35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 1 deletions

View File

@ -282,6 +282,11 @@ Standard names are defined for the following types:
.. versionadded:: 3.3
.. versionchanged:: 3.9
Updated to support the new union (``|``) operator from :pep:`584`, which
simply delegates to the underlying mapping.
.. describe:: key in proxy
Return ``True`` if the underlying mapping has a key *key*, else

View File

@ -622,8 +622,11 @@ class MappingProxyTests(unittest.TestCase):
self.assertEqual(attrs, {
'__contains__',
'__getitem__',
'__ior__',
'__iter__',
'__len__',
'__or__',
'__ror__',
'copy',
'get',
'items',
@ -774,6 +777,22 @@ class MappingProxyTests(unittest.TestCase):
self.assertEqual(view['key1'], 70)
self.assertEqual(copy['key1'], 27)
def test_union(self):
mapping = {'a': 0, 'b': 1, 'c': 2}
view = self.mappingproxy(mapping)
with self.assertRaises(TypeError):
view | [('r', 2), ('d', 2)]
with self.assertRaises(TypeError):
[('r', 2), ('d', 2)] | view
with self.assertRaises(TypeError):
view |= [('r', 2), ('d', 2)]
other = {'c': 3, 'p': 0}
self.assertDictEqual(view | other, {'a': 0, 'b': 1, 'c': 3, 'p': 0})
self.assertDictEqual(other | view, {'c': 2, 'p': 0, 'a': 0, 'b': 1})
self.assertEqual(view, {'a': 0, 'b': 1, 'c': 2})
self.assertDictEqual(mapping, {'a': 0, 'b': 1, 'c': 2})
self.assertDictEqual(other, {'c': 3, 'p': 0})
class ClassCreationTests(unittest.TestCase):

View File

@ -0,0 +1,2 @@
:class:`types.MappingProxyType` objects now support the merge (``|``) operator
from :pep:`584`.

View File

@ -982,6 +982,30 @@ static PyMappingMethods mappingproxy_as_mapping = {
0, /* mp_ass_subscript */
};
static PyObject *
mappingproxy_or(PyObject *left, PyObject *right)
{
if (PyObject_TypeCheck(left, &PyDictProxy_Type)) {
left = ((mappingproxyobject*)left)->mapping;
}
if (PyObject_TypeCheck(right, &PyDictProxy_Type)) {
right = ((mappingproxyobject*)right)->mapping;
}
return PyNumber_Or(left, right);
}
static PyObject *
mappingproxy_ior(PyObject *self, PyObject *Py_UNUSED(other))
{
return PyErr_Format(PyExc_TypeError,
"'|=' is not supported by %s; use '|' instead", Py_TYPE(self)->tp_name);
}
static PyNumberMethods mappingproxy_as_number = {
.nb_or = mappingproxy_or,
.nb_inplace_or = mappingproxy_ior,
};
static int
mappingproxy_contains(mappingproxyobject *pp, PyObject *key)
{
@ -1717,7 +1741,7 @@ PyTypeObject PyDictProxy_Type = {
0, /* tp_setattr */
0, /* tp_as_async */
(reprfunc)mappingproxy_repr, /* tp_repr */
0, /* tp_as_number */
&mappingproxy_as_number, /* tp_as_number */
&mappingproxy_as_sequence, /* tp_as_sequence */
&mappingproxy_as_mapping, /* tp_as_mapping */
0, /* tp_hash */