]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-36144: Update MappingProxyType with PEP 584's operators (#18814)
authorBrandt Bucher <brandtbucher@gmail.com>
Sat, 7 Mar 2020 19:03:09 +0000 (11:03 -0800)
committerGitHub <noreply@github.com>
Sat, 7 Mar 2020 19:03:09 +0000 (11:03 -0800)
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.

Doc/library/types.rst
Lib/test/test_types.py
Misc/NEWS.d/next/Library/2020-02-25-09-28-06.bpo-36144.Rbvvi7.rst [new file with mode: 0644]
Objects/descrobject.c

index 3529c2b0edb896630fdf6c9a78ddff033f9114b3..4cb91c1a90bcfced6106ae4e1e4b80e55c911dcc 100644 (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
index 7b45b7a589503913360896b2335f597666c86868..544c91bc36a2a843c60fb92f410d822dba705385 100644 (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):
 
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 (file)
index 0000000..da0ff9d
--- /dev/null
@@ -0,0 +1,2 @@
+:class:`types.MappingProxyType` objects now support the merge (``|``) operator
+from :pep:`584`.
index c96945bdb1f316608c155cd809e9f7069d29354e..4ebbb74151a232478633cb87bbb3f4119ba6ba09 100644 (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 */