Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 4663f66

Browse files
authoredMar 7, 2020
bpo-36144: Update MappingProxyType with PEP 584's operators (python#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.
1 parent 8f13053 commit 4663f66

File tree

4 files changed

+51
-1
lines changed

4 files changed

+51
-1
lines changed
 

‎Doc/library/types.rst

+5
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ Standard names are defined for the following types:
282282

283283
.. versionadded:: 3.3
284284

285+
.. versionchanged:: 3.9
286+
287+
Updated to support the new union (``|``) operator from :pep:`584`, which
288+
simply delegates to the underlying mapping.
289+
285290
.. describe:: key in proxy
286291

287292
Return ``True`` if the underlying mapping has a key *key*, else

‎Lib/test/test_types.py

+19
Original file line numberDiff line numberDiff line change
@@ -622,8 +622,11 @@ def test_methods(self):
622622
self.assertEqual(attrs, {
623623
'__contains__',
624624
'__getitem__',
625+
'__ior__',
625626
'__iter__',
626627
'__len__',
628+
'__or__',
629+
'__ror__',
627630
'copy',
628631
'get',
629632
'items',
@@ -774,6 +777,22 @@ def test_copy(self):
774777
self.assertEqual(view['key1'], 70)
775778
self.assertEqual(copy['key1'], 27)
776779

780+
def test_union(self):
781+
mapping = {'a': 0, 'b': 1, 'c': 2}
782+
view = self.mappingproxy(mapping)
783+
with self.assertRaises(TypeError):
784+
view | [('r', 2), ('d', 2)]
785+
with self.assertRaises(TypeError):
786+
[('r', 2), ('d', 2)] | view
787+
with self.assertRaises(TypeError):
788+
view |= [('r', 2), ('d', 2)]
789+
other = {'c': 3, 'p': 0}
790+
self.assertDictEqual(view | other, {'a': 0, 'b': 1, 'c': 3, 'p': 0})
791+
self.assertDictEqual(other | view, {'c': 2, 'p': 0, 'a': 0, 'b': 1})
792+
self.assertEqual(view, {'a': 0, 'b': 1, 'c': 2})
793+
self.assertDictEqual(mapping, {'a': 0, 'b': 1, 'c': 2})
794+
self.assertDictEqual(other, {'c': 3, 'p': 0})
795+
777796

778797
class ClassCreationTests(unittest.TestCase):
779798

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

‎Objects/descrobject.c

+25-1
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,30 @@ static PyMappingMethods mappingproxy_as_mapping = {
982982
0, /* mp_ass_subscript */
983983
};
984984

985+
static PyObject *
986+
mappingproxy_or(PyObject *left, PyObject *right)
987+
{
988+
if (PyObject_TypeCheck(left, &PyDictProxy_Type)) {
989+
left = ((mappingproxyobject*)left)->mapping;
990+
}
991+
if (PyObject_TypeCheck(right, &PyDictProxy_Type)) {
992+
right = ((mappingproxyobject*)right)->mapping;
993+
}
994+
return PyNumber_Or(left, right);
995+
}
996+
997+
static PyObject *
998+
mappingproxy_ior(PyObject *self, PyObject *Py_UNUSED(other))
999+
{
1000+
return PyErr_Format(PyExc_TypeError,
1001+
"'|=' is not supported by %s; use '|' instead", Py_TYPE(self)->tp_name);
1002+
}
1003+
1004+
static PyNumberMethods mappingproxy_as_number = {
1005+
.nb_or = mappingproxy_or,
1006+
.nb_inplace_or = mappingproxy_ior,
1007+
};
1008+
9851009
static int
9861010
mappingproxy_contains(mappingproxyobject *pp, PyObject *key)
9871011
{
@@ -1717,7 +1741,7 @@ PyTypeObject PyDictProxy_Type = {
17171741
0, /* tp_setattr */
17181742
0, /* tp_as_async */
17191743
(reprfunc)mappingproxy_repr, /* tp_repr */
1720-
0, /* tp_as_number */
1744+
&mappingproxy_as_number, /* tp_as_number */
17211745
&mappingproxy_as_sequence, /* tp_as_sequence */
17221746
&mappingproxy_as_mapping, /* tp_as_mapping */
17231747
0, /* tp_hash */

0 commit comments

Comments
 (0)
Please sign in to comment.