From 60c3d3551a96febac7b6016fb44605643842c686 Mon Sep 17 00:00:00 2001
From: Serhiy Storchaka <storchaka@gmail.com>
Date: Sat, 11 Nov 2017 16:19:56 +0200
Subject: [PATCH] bpo-31572: Get rid of _PyObject_HasAttrId() in dict and
 OrderedDict. (#3728)

Silence only AttributeError when get "key" and "items" attributes in
the constructor and the update() method of dict and OrderedDict .
---
 Objects/dictobject.c  | 17 +++++++++++----
 Objects/odictobject.c | 51 ++++++++++++++++++++++++++++---------------
 2 files changed, 47 insertions(+), 21 deletions(-)

diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 779746a31aa..b20b85c909e 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -2183,16 +2183,25 @@ dict_update_common(PyObject *self, PyObject *args, PyObject *kwds,
     PyObject *arg = NULL;
     int result = 0;
 
-    if (!PyArg_UnpackTuple(args, methname, 0, 1, &arg))
+    if (!PyArg_UnpackTuple(args, methname, 0, 1, &arg)) {
         result = -1;
-
+    }
     else if (arg != NULL) {
         _Py_IDENTIFIER(keys);
-        if (_PyObject_HasAttrId(arg, &PyId_keys))
+        PyObject *func = _PyObject_GetAttrId(arg, &PyId_keys);
+        if (func != NULL) {
+            Py_DECREF(func);
             result = PyDict_Merge(self, arg, 1);
-        else
+        }
+        else if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+            PyErr_Clear();
             result = PyDict_MergeFromSeq2(self, arg, 1);
+        }
+        else {
+            result = -1;
+        }
     }
+
     if (result == 0 && kwds != NULL) {
         if (PyArg_ValidateKeywordArguments(kwds))
             result = PyDict_Merge(self, kwds, 1);
diff --git a/Objects/odictobject.c b/Objects/odictobject.c
index 5d22ce71588..218aa2c5d9c 100644
--- a/Objects/odictobject.c
+++ b/Objects/odictobject.c
@@ -2343,15 +2343,12 @@ mutablemapping_update(PyObject *self, PyObject *args, PyObject *kwargs)
     }
 
     if (len) {
+        PyObject *func;
         PyObject *other = PyTuple_GET_ITEM(args, 0);  /* borrowed reference */
         assert(other != NULL);
         Py_INCREF(other);
-        if PyDict_CheckExact(other) {
-            PyObject *items;
-            if (PyDict_CheckExact(other))
-                items = PyDict_Items(other);
-            else
-                items = _PyObject_CallMethodId(other, &PyId_items, NULL);
+        if (PyDict_CheckExact(other)) {
+            PyObject *items = PyDict_Items(other);
             Py_DECREF(other);
             if (items == NULL)
                 return NULL;
@@ -2359,10 +2356,14 @@ mutablemapping_update(PyObject *self, PyObject *args, PyObject *kwargs)
             Py_DECREF(items);
             if (res == -1)
                 return NULL;
+            goto handle_kwargs;
         }
-        else if (_PyObject_HasAttrId(other, &PyId_keys)) {  /* never fails */
+
+        func = _PyObject_GetAttrId(other, &PyId_keys);
+        if (func != NULL) {
             PyObject *keys, *iterator, *key;
-            keys = _PyObject_CallMethodIdObjArgs(other, &PyId_keys, NULL);
+            keys = _PyObject_CallNoArg(func);
+            Py_DECREF(func);
             if (keys == NULL) {
                 Py_DECREF(other);
                 return NULL;
@@ -2388,29 +2389,45 @@ mutablemapping_update(PyObject *self, PyObject *args, PyObject *kwargs)
             Py_DECREF(iterator);
             if (res != 0 || PyErr_Occurred())
                 return NULL;
+            goto handle_kwargs;
+        }
+        else if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
+            Py_DECREF(other);
+            return NULL;
+        }
+        else {
+            PyErr_Clear();
         }
-        else if (_PyObject_HasAttrId(other, &PyId_items)) {  /* never fails */
+
+        func = _PyObject_GetAttrId(other, &PyId_items);
+        if (func != NULL) {
             PyObject *items;
-            if (PyDict_CheckExact(other))
-                items = PyDict_Items(other);
-            else
-                items = _PyObject_CallMethodId(other, &PyId_items, NULL);
             Py_DECREF(other);
+            items = _PyObject_CallNoArg(func);
+            Py_DECREF(func);
             if (items == NULL)
                 return NULL;
             res = mutablemapping_add_pairs(self, items);
             Py_DECREF(items);
             if (res == -1)
                 return NULL;
+            goto handle_kwargs;
         }
-        else {
-            res = mutablemapping_add_pairs(self, other);
+        else if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
             Py_DECREF(other);
-            if (res != 0)
-                return NULL;
+            return NULL;
         }
+        else {
+            PyErr_Clear();
+        }
+
+        res = mutablemapping_add_pairs(self, other);
+        Py_DECREF(other);
+        if (res != 0)
+            return NULL;
     }
 
+  handle_kwargs:
     /* now handle kwargs */
     assert(kwargs == NULL || PyDict_Check(kwargs));
     if (kwargs != NULL && PyDict_GET_SIZE(kwargs)) {
-- 
2.30.9