diff --git a/Doc/library/types.rst b/Doc/library/types.rst index 3529c2b0edb..4cb91c1a90b 100644 --- a/Doc/library/types.rst +++ b/Doc/library/types.rst @@ -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 diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 7b45b7a5895..544c91bc36a 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -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): diff --git a/Misc/NEWS.d/next/Library/2020-02-25-09-28-06.bpo-36144.Rbvvi7.rst b/Misc/NEWS.d/next/Library/2020-02-25-09-28-06.bpo-36144.Rbvvi7.rst new file mode 100644 index 00000000000..da0ff9d9ff8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-02-25-09-28-06.bpo-36144.Rbvvi7.rst @@ -0,0 +1,2 @@ +:class:`types.MappingProxyType` objects now support the merge (``|``) operator +from :pep:`584`. diff --git a/Objects/descrobject.c b/Objects/descrobject.c index c96945bdb1f..4ebbb74151a 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -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 */